# RYU: Lab 1 Application NAT ^ Objet | contrôleur RYU avec NAT | ^ Niveau requis | débutant, avisé | ^ Débutant, à savoir | [[reseau:ryu-overview]] | ^ Suivi | :DRAFT: | ## Objectif Cette implémentation utilise Ryu NAT ( https://github.com/John-Lin/nat ) pour effectuer un comportement NAT afin de comprendre les opérations NAT. ## Topologie expérimentale ### Télécharger le code NAT depuis Github ``` $ git clone https://github.com/John-Lin/nat $ cd nat ``` ### Initialiser les paramètres Avant d'iniliaser la configuration modifier les paramètres par défaut dans la méthode nat\_config\_init en fonction de l'environnement d'expérimentation dans `snat.py`: ``` 393 @route('nat_setings_init', urls.url_nat_config_init, methods='POST') 394 def nat_config_init(self, req, **kwargs): 395 save-dict = {} 396 397 # Default settings modify these value to fit your requirements 393 save_dict['wan_port'] = 1 399 save_dict['nat_public_ip'] = IPAddress('140.116.71.178') 400 save_dict['default_gateway'] = IPAddress('140.114.71.254') 401 402 network = '192.168.8.0/24' 403 save_dict['ip_network'] = IPNetwork(network) 404 save_dict['dhcp_gw_addr'] = IPNetwork(network)[1] 405 406 save_dict['broadcast_addr'] = IPAddress('192.168.8.255') 407 save_dict['dns_addr'] = IPAddress('8.8.8.8') 403 409 save_dict['dhcp_hw_addr'] = '08.00:27.8:0f:gd' 410 save_dict['MAC_ON_WAN'] = '00:0e:c6:67:ag:f6' 411 save_dict['MAC_ON_LAN'] = '00:0e:c6:87:ag:fa' ``` | Wan\_port | Cela correspond au port du commutateur et est défini sur le port externe au NAT. | | Nat\_public\_ip | NAT divulgué publiquement | | Default\_gateway | L'adresse de passerelle du même domaine que l'adresse IP publique NAT | | Network | Domaine LAN dans NAT / 24 (plus masque) | | Ip\_network | Domaine LAN dans NAT | | Dhcp\_gw\_addr | Cet auteur doit faire un dhcp simple, il y a donc une adresse de passerelle dhcp | | Broadcast\_addr | Adresse de diffusion utilisée par dhcp | | Dns\_addr | Adresse DNS | | Dhcp\_hw\_addr | adresse MAC Dhcp| | MAC\_ON\_WAN | adresse MAC WAN | | MAC\_ON\_LAN | adresse MAC LAN | * Lancer Ryu NAT ``` $ ryu-manager base.py l2switch.py dhcp.py snat.py --verbose ``` * Initialiser Ryu NAT Cela initialisera la configuration NAT ``` curl --noproxy xx.xx.xxx.xxx -X POST -d ' {}' http://xx.xx.xxx.xxx:8383/api/nat_config_init ``` ### Lancer Ryu NAT ``` $ ryu-manager base.py l2switch.py dhcp.py snat.py --verbose ``` ### Mettre à jour les paramètres NAT ^ Méthode | PUT | ^ URI | /api/nat\_config\_save| Cela mettra à jour la configuration NAT ``` $ curl --noproxy xx.xx.xxx.xxx -X PUT -d ' { "wanPort": 4, "natPublicIp": "xx.xx.xxx.xxx", "defaultGateway": "xx.xx.xxx.x", "natPrivateNetwork": "192.168.8.0" }' http://xx.xx.xxx.xxx:8383/api/nat_config_save ``` ## Expérimentation Essayer d’exécuter les instructions Ping sur l’hôte 2 à partir de l’hôte 1. ``` Ping H1: $ ping 192.168.1.10 ``` On peux voir que Ping ne peut pas passer. ### Analyse du code dans snat.py Le traitement des paquets ICMP a été prévu mais qu'il n'a pas été implémenté: ``` 292 @set_evcls (ofp_event.EventOFPPacketIn, MAIN_DISPATCHER) 293 def_packet_in_handler (self, ev). 294 msg = ev.msg 295 datapath = msg.datapath 296 # ofproto = datapath.ofproto 297 # parser = datapath.ofproto_parser 293 in_port = msg.match['rin_port'] 299 pkt = packet.Packet(msg.data) 300 301 pkt_ethernet = pkt_protocol(ethernet.ethernet) 302 pkt_arp = pkt.get_protocol(arp.arp) 303 # pkt_icmp = pkt.get_protoco(icmp.icmp) 304 pkt_ip = pkt.get_protocol(ipv4.ipv4) 305 pkt_tcp = pkt.get_protocol(tcp.tcp) 306 pkt_udp = pkt.get_protocol(udp.udp) ``` ``` if pkt_tcp: # print "@@@ Install TCP Flow Entry @@@" tcp_src = pkt_tcp.src_port tcp_dst = pkt_tcp.dst_port match = parser.OFPMatch(in_port=in_port, eth_type=ether.ETH_TYPE_IP, ip_proto=inet.IPPROTO_TCP, ipv4_src=ipv4_src, ipv4_dst=ipv4_dst, tcp_src=tcp_src, tcp_dst=tcp_dst) actions = [parser.OFPActionSetField(eth_dst=IP_TO_MAC_TABLE[target_ip]), parser.OFPActionSetField(ipv4_src=self.nat_public_ip), parser.OFPActionSetField(tcp_src=nat_port), parser.OFPActionOutput(out_port)] match_back = parser.OFPMatch(eth_type=ether.ETH_TYPE_IP, ip_proto=inet.IPPROTO_TCP, ipv4_src=ipv4_dst, ipv4_dst=self.nat_public_ip, tcp_src=tcp_dst, tcp_dst=nat_port) actions_back = [parser.OFPActionSetField(eth_dst=eth_src), parser.OFPActionSetField(ipv4_dst=ipv4_src), parser.OFPActionSetField(tcp_dst=tcp_src), parser.OFPActionOutput(in_port)] elif pkt_udp: # print "@@@ Install UDP Flow Entry @@@" udp_src = pkt_udp.src_port udp_dst = pkt_udp.dst_port match = parser.OFPMatch(in_port=in_port, eth_type=ether.ETH_TYPE_IP, ip_proto=inet.IPPROTO_UDP, ipv4_src=ipv4_src, ipv4_dst=ipv4_dst, udp_src=udp_src, udp_dst=udp_dst) actions = [parser.OFPActionSetField(eth_dst=IP_TO_MAC_TABLE[target_ip]), parser.OFPActionSetField(ipv4_src=self.nat_public_ip), parser.OFPActionSetField(udp_src=nat_port), parser.OFPActionOutput(out_port)] match_back = parser.OFPMatch(eth_type=ether.ETH_TYPE_IP, ip_proto=inet.IPPROTO_UDP, ipv4_src=ipv4_dst, ipv4_dst=self.nat_public_ip, udp_src=udp_dst, udp_dst=nat_port) actions_back = [parser.OFPActionSetField(eth_dst=eth_src), parser.OFPActionSetField(ipv4_dst=ipv4_src), parser.OFPActionSetField(udp_dst=udp_src), parser.OFPActionOutput(in_port)] else: pass ``` Par conséquent, lorsque l'instruction Flow Entry est exécutée, il n'y a pas de partie ICMP. Pour faire passer la commande Ping efficacement, Il faut donc activer la résolution de la partie ICMP. ### Activation de la section ICMP La première étape consiste donc à obtenir les informations d'en-tête ICMP en activant la prise en charge de protocole ICMP `Pkt\_icmp = pkt.get\_protocol (icmp.icmp)` dans le code : ``` set_ev_cls(ofp_event.EventoFPPacketIn, MAIN_DISPATCHER) ef (self, ev), msg = ev.msg datapath = msg.datapath ofproto = datapath.ofproto parser = datapath.ofproto_parser in_port = msg.matchr['in_port'] pkt = packet.Packet(msg.data) pkt_ethernet = pkt.get_protocol(ethernet.ethernet) pkt_arp = pkt.get_protoco/(arp.arp) Pkt_icmip= pkt.get_protocol(icmp.icmp) pkt_ip = pkt.get_protocol(ipv4.ipv4) pkt_tcp = pkt.get_protocol(tcp.tcp) pkt_udp = pkt.get_protocol(udp.udp) ``` Ensuite, lorsqu'on a déterminé qu'il s'agit d'un paquet ICMP, les instructions doivent être données au commutateur. ``` elif pkt_icmp: print "@@@ Install ICMP Flow Entry @@@" match = parser.OFPMatch(in_port=in_port, eth_type=ether.ETH_TYPE_IP, ip_proto=inet.IPPROTO_ICMP, ipv4_src=ipv4_src, ipv4_dst=ipv4_dst, ) actions = [parser.OFPActionSetField(eth_dst=IP_TO_MAC_TABLE[target_ip]), parser.OFPActionSetField(ipv4_src=self.nat_public_ip), parser.OFPActionOutput(out_port)] match_back = parser.OFPMatch(eth_type=ether.ETH_TYPE_IP, ip_proto=inet.IPPROTO_ICMP, ipv4_src=ipv4_dst, ipv4_dst=self.nat_public_ip, ) actions_back = [parser.OFPActionSetField(eth_dst=eth_src), parser.OFPActionSetField(ipv4_dst=ipv4_src), parser.OFPActionOutput(in_port)] else: pass ``` Ensuite, il faut déterminer si l'adresse cible est dans ARP TABLE. ``` if pkt_tcp: if target_ip in IP_TO_MAC_TABLE: self._private_to-public(datapath=datapath, buffer_id=msg.buffer_id, data=msg.data, in_port=in_Port, out-port=self.wan_Port, pkt_ethernet=pkt_ethernet, pkt_ip=pkt_ip, pkt_tcp=pkt_tcp) elif pkt_udp: if target_ip in IP_TO_MAC_TABLE: self._private_to_public(datapath=datapath, buffer_id=msg.buffer_id, data=msg.data, in_port=in_Port, out-port=self.wan_Port, pkt_ethernet=pkt_ethernet, pkt_ip=pkt_ip, pkt_udp=pkt_udp) elif pkt_icmp: if target_ip in IP_TO_MAC_TABLE: self._private_to_public(datapath=datapath, buffer_id=msg.buffer_id, data=msg.data, in_port=in_Port, out-port=self.wan_Port, pkt_ethernet=pkt_ethernet, pkt_ip=pkt_ip, pkt_udp=pkt_icmp) ``` Relancer Ryu NAT ``` $ ryu-manager base.py l2switch.py dhcp.py snat.py --verbose ``` Essayer à nouveau d'utiliser la commande Ping de H1 à H2. ``` H1: $ ping 192.168.1.10 ``` Peut être cette fois, cela passera