From 8fefb88cac93e00ea4f94b43775990c277d16564 Mon Sep 17 00:00:00 2001 From: Your Name Date: Thu, 24 Aug 2017 17:34:04 +0200 Subject: [PATCH] Added ipv6 automatic configuration use case through gRPC --- ipv6_conf/.gitignore | 1 + ipv6_conf/Report.md | 246 ++++++++++++++++++++++++++++++++++++ ipv6_conf/client.py | 98 ++++++++++++++ ipv6_conf/ctrl_ipv6_conf.sh | 7 + ipv6_conf/ipset.proto | 23 ++++ ipv6_conf/ipset_pb2.py | 152 ++++++++++++++++++++++ ipv6_conf/ipset_pb2_grpc.py | 46 +++++++ ipv6_conf/server.py | 60 +++++++++ 8 files changed, 633 insertions(+) create mode 100644 ipv6_conf/.gitignore create mode 100644 ipv6_conf/Report.md create mode 100644 ipv6_conf/client.py create mode 100755 ipv6_conf/ctrl_ipv6_conf.sh create mode 100644 ipv6_conf/ipset.proto create mode 100644 ipv6_conf/ipset_pb2.py create mode 100644 ipv6_conf/ipset_pb2_grpc.py create mode 100644 ipv6_conf/server.py diff --git a/ipv6_conf/.gitignore b/ipv6_conf/.gitignore new file mode 100644 index 0000000..0d20b64 --- /dev/null +++ b/ipv6_conf/.gitignore @@ -0,0 +1 @@ +*.pyc diff --git a/ipv6_conf/Report.md b/ipv6_conf/Report.md new file mode 100644 index 0000000..406ca98 --- /dev/null +++ b/ipv6_conf/Report.md @@ -0,0 +1,246 @@ +# Introduzione e scopo del progetto +Il presente lavoro ha l'obiettivo di fare il design, implementare e testare un'applicazione +su ambiente OSHI che permetta la configurazione automatica di indirizzi ipv6 su una topologia +relativamente semplice. A questo scopo è stata utilizzata la tecnologia [gRPC](grpc.io). +Essenzialmente è un framework multipiattaforma che permette di eseguire procedure su un server +remoto in modo scalabile e veloce. + +Nel seguito analizzeremo i passaggi che ci hanno permesso di raggiungere l'obbiettivo. + +# Deploy +In questa sezione analizziamo in dettaglio i principali passaggi che hanno +permesso l'inizializzazione dei server gRPC in ogni nodo della rete. + +Prima di tutto avevamo la necessità di comunicare al *deployer* della topologia +l'utilizzo dei server gRPC. A tale scopo è stata creata una semplice struttura +dati da inserire manualmente nell'output json della topologia in questione: + + "grpc_ipv6": { + "path": "/path/del/server/grpc.py" + } + +L'unico attributo **"path"** indica il percorso del file python dove è +implementato il server gRPC. + +In seguito abbiamo apportato delle modifiche al file python relativo al parsing +del codice JSON generato nell'interfaccia web: + +*~/workspace/Dreamer-Topology-Parser-and-Validator/topo_parser.py*: + + def parse_data(self): + self.load_advanced() + self.load_vertex() + self.load_links() + self.load_vss() + self.create_subnet() + self.load_grpc() # load gRPC path + self.parsed = True + ... + def load_grpc(self): + if self.verbose: + print "*** Retrieve Grpc Options" + grpc_ipv6 = self.json_data['grpc_ipv6'] if 'grpc_ipv6' in self.json_data else [] + self.path_grpc = grpc_ipv6['path'] + +La classe *TopoParser* viene istanziata nel file *~/workspace/Dreamer-Mininet-Extensions/mininet_deployer.py* che si occupa del +deploy della rete creata nell'interfaccia web, ovvero dell'instaurazione dei nodi e +dei links nell'emulatore Mininet. In particolare sono state aggiunte queste righe +nella creazione della rete: + +*mininet_deployer.py* + + if parser.path_grpc != "": + net.grpc_path = parser.path_grpc + +Come spiegato nel dettaglio a seguire, grazie a questa modifica assegnamo al campo +*grpc_path* del costruttore dell'estensione custom della classe Mininet, la stringa +contenente il path del server gRPC. + +La definizione dell'estensione della classe Mininet si trova nel file *~/workspace/Dreamer-Mininet-Extensions/mininet_extensions.py* dove sono definiti +anche tutti i nodi personalizzati dell'ambiente OSHI. In questa classe, in +particolare nel metodo *start()*, avviene la vera e propria instaurazione dei +server gRPC in tutti i nodi: + +*mininet_extensions.py* + + if self.grpc_path != "": + all_nodes = self.cr_oshis + self.pe_oshis + self.ce_routers + start_server = ["python",self.grpc_path,"&"] + print "Starting grpc servers with %s" % start_server + for node in all_nodes: + node_name = node.name + node.popen(start_server) # with cmd (for some unsolved reason) we were not able to run python + print "*** Starting grpc server on %s at %s" % (node_name, node.IP()) + +Inizialmente nell'array *all_nodes* vengono salvati i riferimenti agli +oggetti "nodo" (da notare che abbiamo volutamente escluso i controllers in +quato questi sarebbero i client del sistema gRPC) e salviamo in *start_server* +il comando bash per eseguire i server. In seguito nel costrutto *foreach* +cicliamo su tutti i nodi e in questi facciamo partire il server con il comando +**node.popen(start_server)**. + +A questo proposito è doveroso aprire una parentesi: +il comando che viene utilizzato solitamente nei nodi mininet **node.cmd("")**, +non funzionava per il comando assegnato. Facendo diversi test ci siamo accorti +che, per qualche motivo che ancora non ci è noto, questo comando non esegue +applicativi python, ma tutto il resto sì. La soluzione più veloce è stata quella +di utilizzare per l'appunto il metodo sopracitato che, ai nostri fini, era +equivalente. + +A questo punto, una volta effettuato il deploy della topologia, tutti i nodi +hanno il proprio server gRPC avviato, il cui funzionamento verrà esposto nella +sezione seguente. + +# gRPC + +Il motivo per cui usiamo gRPC è molto semplice: estendere le funzionalità di openflow tramite la tecnologia rpc. +Una soluzione alternativa sarebbe stata quella di usare le RESTapi ma in termini di prestazioni gRPC risulta nettamente superiore. + +Al momento del deploy della rete ogni nodo, ad esclusione del controller, apre un socket sulla porta scelta per far comunicare server e client grpc (si è scelta la porta 50001). Precisamente il controller svolgerà la funzione di client mentre tutti gli altri nodi avranno la funzione di server. Questo perchè il controller vuole effettuare delle operazioni sui nodi che gestisce normalmente tramite openflow estendendo così le sue funzionalità con una tecnologia altrettanto prestazionale. + +Un ulteriore vantaggio riscontrato è la semplicità del codice. +gRPC infatti, oltre ad avere una documentazione molto dettagliata, permette con facilità di realizzare un servizio utilizzando il linguaggio di programmazione che si desidera con poche righe e molto intuitivamente. La nostrà scelta è ricaduta su python. Vediamone i dettagli: + +1) Si parte da un file con estensione .proto (qui si utilizza il linguaggio proto3). In questo andranno esplicitati sia i metodi di richiesta e risposta tra server e client sia i parametri che si vogliono controllare durante la comunicazione. Nel nostro caso, dato che abbiamo la necessità di configurare indirizzi ipv6 e le rotte sui nodi, si controlleranno i parametri **ipv6, subnet, interface** per ipv6 e **r_subnets, routes, r_devs** per le rotte. Il campo **mode** verrà utilizzato dal client e dal server per scegliere il tipo di servizio da utilizzare, o meglio scegliere se settare: indirizzi ipv6, rotte ipv6, indirizzi e rotte ipv6. + +*ipset.proto* + + ... + message Request { + repeated string ipv6 = 1; + string subnet = 2; + repeated string iface = 3; + repeated string r_subnets = 4; + repeated string routes = 5; + repeated string r_devs = 6; + int32 mode = 7; + } + + message Reply { + string message = 1; + } + ... + +Successivamente il file viene compilato con gli appositi tool che python mette a disposizione, nel caso specifico: + + python -m grpc_tools.protoc -I. --python_out=. --grpc_python_out=. ipset.proto + +Da qui vengono generati 2 file: **ipset_pb2.py** e **ipset_pb2_grpc.py** che contengono le classi e le funzioni necessarie per il passo successivo. + +2) Ora dobbiamo creare il client ed il server utilizzando le 2 librerie generate al passo precendente. + +*client.py* + + def run(remote_ipv4,ipv6,subnet,iface,r_subnets,routes,r_devs,mode): + ok = False + while not ok: + try: + channel = grpc.insecure_channel(remote_ipv4+":50001") + stub = ipset_pb2_grpc.IpSetStub(channel) + response = stub.Set(ipset_pb2.Request(ipv6=ipv6,subnet=subnet,iface=iface, + r_subnets=r_subnets, routes=routes, r_devs=r_devs, + mode=mode)) + except grpc._channel._Rendezvous as e: + print e + time.sleep(1) + print "Retrying..." + else: + print "Server at {} says: {}".format(remote_ipv4,response.message) + ok = True + +Questo è la funzione chiave presente nel client. Volendo comunicare con i server ha bisogno dei loro indirizzi ipv4 e la porta scelta per la comunicazione gRPC. Da qui, se la richiesta va a buon fine, viene ricevuta una risposta di avvenuta comunicazione con l'ulteriore conferma che l'operazione che si voleva svolgere è andata a buon fine. + +*server.py* + + ... + class GiveMe(ipset_pb2_grpc.IpSetServicer): + + def Set(self, request, context): + if request.mode == MODE_ADDR: + set_addrs(request.ipv6, request.subnet, request.iface) + return ipset_pb2.Reply(message='I have set {} on subnet /{} and interface {}' + .format(request.ipv6,request.subnet,request.iface)) + + elif request.mode == MODE_ROUTES: + set_routes(request.r_subnets, request.routes, request.r_devs) + return ipset_pb2.Reply(message='I have set the routes') + + elif request.mode == MODE_ADDR_ROUTES: + set_addrs(request.ipv6, request.subnet, request.iface) + set_routes(request.r_subnets, request.routes, request.r_devs) + return ipset_pb2.Reply(message='I have set {} on subnet /{} and interface {} and set the routes' + .format(request.ipv6,request.subnet,request.iface)) + + else: + return ipset_pb2.Reply(message="Unrecognized mode") + + + def set_addrs(ipv6, subnet, iface): + for i in range(len(ipv6)): + print "Command: sudo ip -6 addr add "+ipv6[i]+"/"+subnet+" dev "+iface[i] + os.system("sudo ip -6 addr add "+ipv6[i]+"/"+subnet+" dev "+iface[i]) + + def set_routes(r_subnets, routes, r_devs): + os.system('sudo sysctl -w net.ipv6.conf.all.forwarding=1') + for i in range(len(r_subnets)): + print "Command: sudo ip -6 r add "+r_subnets[i]+" via "+routes[i]+" dev "+r_devs[i] + os.system("sudo ip -6 r add "+r_subnet[i]+" via "+routes[i]+" dev "+r_devs[i]) + + def serve(): + server = grpc.server(futures.ThreadPoolExecutor(max_workers=20)) + ipset_pb2_grpc.add_IpSetServicer_to_server(GiveMe(), server) + server.add_insecure_port('[::]:50001') + server.start() + ... + +Il server è un tantino più complicato, ma nulla se si pensa che con poche righe abbiamo fatto svolgere una comunicazione molto efficente e, volendo, scalabile ad ogni servizio ci venga in mente. +Viene aperto un socket sulla porta 50001 in attesa di richieste provenienti dal client. Se si riceve una richiesta si controlla la variabile **mode** per interpretare quali operazioni è necessario svolgere, ossia: assegnazione di indirizzi ipv6, assegnazione di rotte o entrambe. +Vengono a questo punto lanciate le funzioni **set_addrs()** e/o **set_routes** in accordo con la variabile **mode** e successivamente recuperate le variabili spedite tramite richiesta grpc dal client. A questo punto si possono eseguire i comandi: + + ... + os.system("sudo ip -6 addr add "+ipv6[i]+"/"+subnet+" dev "+iface[i]) + ... + ... + os.system("sudo ip -6 r add "+r_subnet[i]+" via "+routes[i]+" dev "+r_devs[i]) + ... + +Viene poi inviata, tramite il metodo **Reply()**, la risposta al mittente dell'avvenuta configurazione richiesta. + + +# DEMO + +In questa sezione descriviamo brevemente il procedimento per testare il lavoro esposto sopra. + +- Una volta eseguita la macchina virtuale OSHI (in particolare abbiamo usato la versione 8), + avviare lo script **GO** sul desktop. Il sistema procederà ad avviare il server e + l'interfaccia web del framework OSHI. +- Bisogna creare un nuovo progetto e utilizzare la topologia d'esempio chiamata *example_network_3cr_2pe_2ce* +- Adesso è necessario apportare una modifica al codice json che descrive la topogia: + 1. Selezionare *edit* nel menu a tendina; + 2. Accedere al codice json; + 3. Aggiungere l'oggetto json come proposto sopra; + 4. Salvare. +- Lanciare un *deployment* della topologia. +- Eseguire un terminale e accedere al controllore attraverso ssh, la password è **root**: + + ssh root@10.255.248.1 + +- Avviare il client gRPC: + + cd /root/ipv6-oshi-grpc/grpc + python client.py + +- Se tutto è andato bene per ogni nodo della rete si dovrebbero ricevere dei messaggi di + avvenuta configurazione +- Eseguire lo script contenuto nel repository per configurare ipv6 sul controllore: + + ./ctrl_ipv6_conf.sh + +- Eseguire dei ping di prova che verifichino il corretto funzionamento della rete ipv6: + + ping6 $node_ip + + + + + diff --git a/ipv6_conf/client.py b/ipv6_conf/client.py new file mode 100644 index 0000000..204a6e4 --- /dev/null +++ b/ipv6_conf/client.py @@ -0,0 +1,98 @@ +import grpc +import ipset_pb2 +import ipset_pb2_grpc +import time + +MODE_ADDR = 1; +MODE_ROUTES = 2; +MODE_ADDR_ROUTES = 3; + +def run(remote_ipv4,ipv6,subnet,iface,r_subnets,routes,r_devs,mode): + ok = False + while not ok: + try: + channel = grpc.insecure_channel(remote_ipv4+":50001") + stub = ipset_pb2_grpc.IpSetStub(channel) + response = stub.Set(ipset_pb2.Request(ipv6=ipv6,subnet=subnet,iface=iface, + r_subnets=r_subnets, routes=routes, r_devs=r_devs, + mode=mode)) + except grpc._channel._Rendezvous as e: + print e + time.sleep(1) + print "Retrying..." + else: + print "Server at {} says: {}".format(remote_ipv4,response.message) + ok = True + + +if __name__ == '__main__': + remote_ipv4_cro3 = "10.0.2.1" # used to contact a node + ipv6_cro3 = ["fd3c:9f20:5d73:03::01","fd3c:9f20:5d73:05::02","fd3c:9f20:5d73:06::02","fd3c:9f20:5d73:07::01","fd3c:9f20:5d73:0b::02"] + subnet_cro3 = "64" + iface_cro3 = ["vi1","vi2","vi3","vi4","vi5"] + r_subnets_cro3 = ["default","fd3c:9f20:5d73:01::/64","fd3c:9f20:5d73:0a::/64"] + routes_cro3 = ["fd3c:9f20:5d73:03::02","fd3c:9f20:5d73:05::01","fd3c:9f20:5d73:06::01"] + r_devs_cro3 = ["vi1","vi2","vi3"] + + remote_ipv4_cro4 = "10.0.1.1" # used to contact a node + ipv6_cro4 = ["fd3c:9f20:5d73:02::02","fd3c:9f20:5d73:07::02","fd3c:9f20:5d73:08::02"] + subnet_cro4 = "64" + iface_cro4 = ["vi1","vi2","vi3"] + r_subnets_cro4 = ["default","fd3c:9f20:5d73:04::/64","fd3c:9f20:5d73:01::/64","fd3c:9f20:5d73:0b::/64"] + routes_cro4 = ["fd3c:9f20:5d73:08::01","fd3c:9f20:5d73:02::01","fd3c:9f20:5d73:02::01","fd3c:9f20:5d73:07::01"] + r_devs_cro4 = ["vi3","vi1","vi1","vi2"] + + remote_ipv4_cro5 = "10.0.2.2" # used to contact a node + ipv6_cro5 = ["fd3c:9f20:5d73:03::02","fd3c:9f20:5d73:04::02","fd3c:9f20:5d73:09::02"] + subnet_cro5 = "64" + iface_cro5 = ["vi1","vi2","vi3"] + r_subnets_cro5 = ["default", "fd3c:9f20:5d73:01::/64", "fd3c:9f20:5d73:0a::/64", "fd3c:9f20:5d73:02::/64", "fd3c:9f20:5d73:08::/64"] + routes_cro5 = ["fd3c:9f20:5d73:03::01", "fd3c:9f20:5d73:04::01", "fd3c:9f20:5d73:09::01", "fd3c:9f20:5d73:04::01","fd3c:9f20:5d73:09::01"] + r_devs_cro5 = ["vi1","vi2", "vi3", "vi2", "vi3"] + + remote_ipv4_peo2 = "10.0.0.2" # used to contact a node + ipv6_peo2 = ["fd3c:9f20:5d73:01::01","fd3c:9f20:5d73:02::01","fd3c:9f20:5d73:04::01","fd3c:9f20:5d73:05::01"] + subnet_peo2 = "64" + iface_peo2 = ["vi1","vi2","vi3","vi4"] + r_subnets_peo2 = ["default","fd3c:9f20:5d73:0b::/64"] + routes_peo2 = ["fd3c:9f20:5d73:04::02","fd3c:9f20:5d73:05::02"] + r_devs_peo2 = ["vi1","vi2","vi3","vi2","vi3"] + + remote_ipv4_peo6 = "10.0.5.1" # used to contact a node + ipv6_peo6 = ["fd3c:9f20:5d73:06::01","fd3c:9f20:5d73:08::01","fd3c:9f20:5d73:09::01","fd3c:9f20:5d73:0a::01"] + subnet_peo6 = "64" + iface_peo6 = ["vi1","vi2","vi3","vi4"] + r_subnets_peo6 = ["default","fd3c:9f20:5d73:0b::/64"] + routes_peo6 = ["fd3c:9f20:5d73:09::02","fd3c:9f20:5d73:06::02"] + r_devs_peo6 = ["vi3","vi1"] + + remote_ipv4_cer1 = "10.0.0.1" # used to contact a node + ipv6_cer1 = ["fd3c:9f20:5d73:01::02"] + subnet_cer1 = "64" + iface_cer1 = ["cer1-eth1"] + r_subnets_cer1 = ["default"] + routes_cer1 = ["fd3c:9f20:5d73:01::01"] + r_devs_cer1 = ["cer1-eth1"] + + remote_ipv4_cer7 = "10.0.9.1" # used to contact a node + ipv6_cer7 = ["fd3c:9f20:5d73:0a::02"] + subnet_cer7 = "64" + iface_cer7 = ["cer7-eth1"] + r_subnets_cer7 = ["default"] + routes_cer7 = ["fd3c:9f20:5d73:0a::01"] + r_devs_cer7 = ["cer7-eth1"] + + print "Trying with cro3" + run(remote_ipv4_cro3,ipv6_cro3,subnet_cro3,iface_cro3,r_subnets_cro3,routes_cro3,r_devs_cro3, MODE_ADDR_ROUTES) + print "Trying with cro4" + run(remote_ipv4_cro4,ipv6_cro4,subnet_cro4,iface_cro4,r_subnets_cro4,routes_cro4,r_devs_cro4, MODE_ADDR_ROUTES) + print "Trying with cro5" + run(remote_ipv4_cro5,ipv6_cro5,subnet_cro5,iface_cro5,r_subnets_cro5,routes_cro5,r_devs_cro5, MODE_ADDR_ROUTES) + print "Trying with peo2" + run(remote_ipv4_peo2,ipv6_peo2,subnet_peo2,iface_peo2,r_subnets_peo2,routes_peo2,r_devs_peo2, MODE_ADDR_ROUTES) + print "Trying with peo6" + run(remote_ipv4_peo6,ipv6_peo6,subnet_peo6,iface_peo6,r_subnets_peo6,routes_peo6,r_devs_peo6, MODE_ADDR_ROUTES) + print "Trying with cer1" + run(remote_ipv4_cer1,ipv6_cer1,subnet_cer1,iface_cer1,r_subnets_cer1,routes_cer1,r_devs_cer1, MODE_ADDR_ROUTES) + print "Trying with cer7" + run(remote_ipv4_cer7,ipv6_cer7,subnet_cer7,iface_cer7,r_subnets_cer7,routes_cer7,r_devs_cer7, MODE_ADDR_ROUTES) diff --git a/ipv6_conf/ctrl_ipv6_conf.sh b/ipv6_conf/ctrl_ipv6_conf.sh new file mode 100755 index 0000000..894252d --- /dev/null +++ b/ipv6_conf/ctrl_ipv6_conf.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +sudo ip -6 addr add fd3c:9f20:5d73:0b::01/64 dev ctr8-eth1 + +sudo sysctl -w net.ipv6.conf.all.forwarding=1 + +sudo ip -6 route add default via fd3c:9f20:5d73:0b::02 dev ctr8-eth1 \ No newline at end of file diff --git a/ipv6_conf/ipset.proto b/ipv6_conf/ipset.proto new file mode 100644 index 0000000..b240c91 --- /dev/null +++ b/ipv6_conf/ipset.proto @@ -0,0 +1,23 @@ +syntax = "proto3"; + +option java_multiple_files = true; + +package ipset; + +service IpSet { + rpc Set (Request) returns (Reply) {} +} + +message Request { + repeated string ipv6 = 1; + string subnet = 2; + repeated string iface = 3; + repeated string r_subnets = 4; + repeated string routes = 5; + repeated string r_devs = 6; + int32 mode = 7; +} + +message Reply { + string message = 1; +} diff --git a/ipv6_conf/ipset_pb2.py b/ipv6_conf/ipset_pb2.py new file mode 100644 index 0000000..16f6816 --- /dev/null +++ b/ipv6_conf/ipset_pb2.py @@ -0,0 +1,152 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: ipset.proto + +import sys +_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database +from google.protobuf import descriptor_pb2 +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor.FileDescriptor( + name='ipset.proto', + package='ipset', + syntax='proto3', + serialized_pb=_b('\n\x0bipset.proto\x12\x05ipset\"w\n\x07Request\x12\x0c\n\x04ipv6\x18\x01 \x03(\t\x12\x0e\n\x06subnet\x18\x02 \x01(\t\x12\r\n\x05iface\x18\x03 \x03(\t\x12\x11\n\tr_subnets\x18\x04 \x03(\t\x12\x0e\n\x06routes\x18\x05 \x03(\t\x12\x0e\n\x06r_devs\x18\x06 \x03(\t\x12\x0c\n\x04mode\x18\x07 \x01(\x05\"\x18\n\x05Reply\x12\x0f\n\x07message\x18\x01 \x01(\t2.\n\x05IpSet\x12%\n\x03Set\x12\x0e.ipset.Request\x1a\x0c.ipset.Reply\"\x00\x42\x02P\x01\x62\x06proto3') +) + + + + +_REQUEST = _descriptor.Descriptor( + name='Request', + full_name='ipset.Request', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='ipv6', full_name='ipset.Request.ipv6', index=0, + number=1, type=9, cpp_type=9, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='subnet', full_name='ipset.Request.subnet', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='iface', full_name='ipset.Request.iface', index=2, + number=3, type=9, cpp_type=9, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='r_subnets', full_name='ipset.Request.r_subnets', index=3, + number=4, type=9, cpp_type=9, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='routes', full_name='ipset.Request.routes', index=4, + number=5, type=9, cpp_type=9, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='r_devs', full_name='ipset.Request.r_devs', index=5, + number=6, type=9, cpp_type=9, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='mode', full_name='ipset.Request.mode', index=6, + number=7, type=5, cpp_type=1, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=22, + serialized_end=141, +) + + +_REPLY = _descriptor.Descriptor( + name='Reply', + full_name='ipset.Reply', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='message', full_name='ipset.Reply.message', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=143, + serialized_end=167, +) + +DESCRIPTOR.message_types_by_name['Request'] = _REQUEST +DESCRIPTOR.message_types_by_name['Reply'] = _REPLY +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + +Request = _reflection.GeneratedProtocolMessageType('Request', (_message.Message,), dict( + DESCRIPTOR = _REQUEST, + __module__ = 'ipset_pb2' + # @@protoc_insertion_point(class_scope:ipset.Request) + )) +_sym_db.RegisterMessage(Request) + +Reply = _reflection.GeneratedProtocolMessageType('Reply', (_message.Message,), dict( + DESCRIPTOR = _REPLY, + __module__ = 'ipset_pb2' + # @@protoc_insertion_point(class_scope:ipset.Reply) + )) +_sym_db.RegisterMessage(Reply) + + +DESCRIPTOR.has_options = True +DESCRIPTOR._options = _descriptor._ParseOptions(descriptor_pb2.FileOptions(), _b('P\001')) +# @@protoc_insertion_point(module_scope) diff --git a/ipv6_conf/ipset_pb2_grpc.py b/ipv6_conf/ipset_pb2_grpc.py new file mode 100644 index 0000000..0494923 --- /dev/null +++ b/ipv6_conf/ipset_pb2_grpc.py @@ -0,0 +1,46 @@ +# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! +import grpc + +import ipset_pb2 as ipset__pb2 + + +class IpSetStub(object): + """The greeting service definition. + """ + + def __init__(self, channel): + """Constructor. + + Args: + channel: A grpc.Channel. + """ + self.Set = channel.unary_unary( + '/ipset.IpSet/Set', + request_serializer=ipset__pb2.Request.SerializeToString, + response_deserializer=ipset__pb2.Reply.FromString, + ) + + +class IpSetServicer(object): + """The greeting service definition. + """ + + def Set(self, request, context): + """Sends a greeting + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + +def add_IpSetServicer_to_server(servicer, server): + rpc_method_handlers = { + 'Set': grpc.unary_unary_rpc_method_handler( + servicer.Set, + request_deserializer=ipset__pb2.Request.FromString, + response_serializer=ipset__pb2.Reply.SerializeToString, + ), + } + generic_handler = grpc.method_handlers_generic_handler( + 'ipset.IpSet', rpc_method_handlers) + server.add_generic_rpc_handlers((generic_handler,)) diff --git a/ipv6_conf/server.py b/ipv6_conf/server.py new file mode 100644 index 0000000..ab652a7 --- /dev/null +++ b/ipv6_conf/server.py @@ -0,0 +1,60 @@ +#!/usr/bin/python + +from concurrent import futures +import time +import os +import grpc +import ipset_pb2 +import ipset_pb2_grpc + +_ONE_DAY_IN_SECONDS = 60 * 60 * 24 +MODE_ADDR = 1; +MODE_ROUTES = 2; +MODE_ADDR_ROUTES = 3; + +class GiveMe(ipset_pb2_grpc.IpSetServicer): + + def Set(self, request, context): + if request.mode == MODE_ADDR: + set_addrs(request.ipv6, request.subnet, request.iface) + return ipset_pb2.Reply(message='I have set {} on subnet /{} and interface {}' + .format(request.ipv6,request.subnet,request.iface)) + + elif request.mode == MODE_ROUTES: + set_routes(request.r_subnets, request.routes, request.r_devs) + return ipset_pb2.Reply(message='I have set the routes') + + elif request.mode == MODE_ADDR_ROUTES: + set_addrs(request.ipv6, request.subnet, request.iface) + set_routes(request.r_subnets, request.routes, request.r_devs) + return ipset_pb2.Reply(message='I have set {} on subnet /{} and interface {} and set the routes' + .format(request.ipv6,request.subnet,request.iface)) + + else: + return ipset_pb2.Reply(message="Unrecognized mode") + + +def set_addrs(ipv6, subnet, iface): + for i in range(len(ipv6)): + print "Command: sudo ip -6 addr add "+ipv6[i]+"/"+subnet+" dev "+iface[i] + os.system("sudo ip -6 addr add "+ipv6[i]+"/"+subnet+" dev "+iface[i]) + +def set_routes(r_subnets, routes, r_devs): + os.system('sudo sysctl -w net.ipv6.conf.all.forwarding=1') + for i in range(len(r_subnets)): + print "Command: sudo ip -6 r add "+r_subnets[i]+" via "+routes[i]+" dev "+r_devs[i] + os.system("sudo ip -6 r add "+r_subnets[i]+" via "+routes[i]+" dev "+r_devs[i]) + +def serve(): + server = grpc.server(futures.ThreadPoolExecutor(max_workers=20)) + ipset_pb2_grpc.add_IpSetServicer_to_server(GiveMe(), server) + server.add_insecure_port('[::]:50001') + server.start() + try: + while True: + time.sleep(_ONE_DAY_IN_SECONDS) + except KeyboardInterrupt: + server.stop(0) + +if __name__ == '__main__': + serve()