Table of Contents
Openflow: Overview
Il est parfois nécessaire que les architectes de réseau (ou les développeurs comme vous) définissent le comportement de leurs réseaux de manière personnalisée. Par exemple, l'architecte peut vouloir un commutateur réseau lui permettant de contrôler la manière dont les paquets sont routés ou même de définir un protocole personnalisé.
Historiquement, cela était possible via un matériel propriétaire fermé qui peut être extrêmement coûteux ou impossible à obtenir par les chercheurs et les expérimentateurs. Pourtant, cette fonctionnalité est nécessaire pour exécuter des projets à grande échelle mettant en œuvre de nouveaux protocoles expérimentaux, voire des règles de routage pour les besoins de l’architecture réseau de base.
OpenFlow est un standard ouvert géré par Open Networking Foundation. Il spécifie un protocole via un commutateur qu'un contrôleur distant peut modifier le comportement des périphériques réseau via un «jeu d'instructions de transmission» bien défini.
Un commutateur OpenFlow est contrôlé par un contrôleur OpenFlow. Un contrôleur OpenFlow est simplement un progiciel qui s'interface avec le commutateur via l'API OpenFlow et applique des règles de routage.
Chaque entrée de flux de données openflow est principalement définie par:
- un objet Match qui spécifie les paquets faisant partie du flux,
- un objet Actions qui peut exécuter des actions sur les ID de port, les ID de file d'attente (Queue) et jusqu'à 4 protocoles.
Description des flux
Le plus simple moyen de coder des instructions avec openflow dans un commutateur est d'utiliser l'API REST fournit en standard par les contrôleurs.
L'outil CURL permet d'utiliser facilement les APIs REST, par exemple pour pousser (PUT) un objet flow dans la configuration d'un commutateur avec opendaylight, on peut soumettre la requète suivante :
curl --noproxy x.x.x.x -u admin:admin -H 'Content-Type: application/yang.data+xml' -X PUT -d ' \ <strict>false</strict> <flow-name>arp2cntrler</flow-name> <id>37</id> \ <cookie_mask>255</cookie_mask> <cookie>105</cookie> <table_id>0</table_id> \ <priority>1</priority> <hard-timeout>0</hard-timeout> <idle-timeout>0</idle-timeout> \ <match> <ethernet-match> <ethernet-type> <type>2054</type> </ethernet-type> </ethernet-match> \ </match> <instructions> <instruction> <order>0</order> <apply-actions> <action> <order>0</order> \ <output-action> <output-node-connector>openflow:506043239089955:33</output-node-connector> \ <max-length>65535</max-length> </output-action> </action> </apply-actions> </instruction> \ </instructions> </flow>' 'http://xx.xx.xxx.xx:8181/restconf/config/opendaylight-inventory:nodes\ /node/openflow:506043239089955/table/0/flow/37'
SI on détaille la commande celle-ci se compose d'un message (en syntaxe xml) que l'on pousse (PUT) vers une URl
L'URL
Se compose (e sus de l'adresse du commutateur et du port d'accès à l'API:
| URI | Identification |
|---|---|
| /restconf/config/opendaylight-inventory:nodes/node/ | openflow:506043239089955/table/0/flow/37 |
L'URI
Réprésente le chemin d'accès relatif à la fonction, par exemple pour pousser un flux:
- avec opendaylight il faut faire un PUT sur
/restconf/config/opendaylight-inventory:nodes/node/ - avec ryu il faut faire un PUT sur
/stats/flowentry/add - avec floodlight il faut faire un PUT sur
/wm/staticflowentrypusher/json
L'identification
Afin d'identifier précisèment un flux le groupe switch(DPID)+table+flux est requis:
- Le DatapathID (DPID) indique sur quel commutateur on veut insérer ce flux.
- L'id du flux doit être unique, Il ne peut y avoir d'autre flux dans la topologie avec le même id.
- table_id Indique dans quelle table le flux doit être recherché/implémenté.
Les champs id et table_id doivent être identiques à ceux codés dans le corps du message.
Selon l'API l'identification est dans l'url , dans le corps du message ou dans les deux
Le corps du message
Se décompose en au moins trois objets
| Header | <?xml version="1.0" encoding="UTF-8" standalone="no"?><flow xmlns="urn:opendaylight:flow:inventory"> |
|---|---|
| Identification | <strict>false</strict><flow-name>arp2cntrler</flow-name><id>37</id> <cookie_mask>255</cookie_mask> <cookie>105</cookie><table_id>0</table_id><priority>1</priority><hard-timeout>0</hard-timeout> <idle-timeout>0</idle-timeout> |
| Champ de match | <match><ethernet-match><ethernet-type><type>2054</type></ethernet-type></ethernet-match></match><instructions><instruction><order>0</order><apply-actions> |
| Champ d'action | <action><order>0</order> <output-action><output-node-connector>FLOOD</output-node-connector><max-length>65535</max-length></output-action> </action></apply-actions> </instruction></instructions> |
</flow> |
L'identification
Le champ id
L'id du flux doit être unique, Il ne peut y avoir d'autre flux dans la topologie avec le même id.
Le champ table_id
Indique dans quelle table le flux doit être recherché/implémenté.
Les champs id et table_id doivent être identiques à ceux codés dans l'url
Le champ "priorité".
Lorsqu'un paquet atteint un commutateur, il parcourt chaque flux en ordre de priorité jusqu'à ce qu'il corresponde à l'un d'entre eux.
L'objet Match
L'objet Match spécifie les paquets faisant partie du flux, sous la forme d'un ensemble de champs de paquet à comparer et de leurs valeurs attendues. La norme OpenFlow spécifie l'ensemble des champs de paquet pouvant être mis en correspondance, au niveau des couches 2 (Ethernet), 3 (IP, ARP) et 4 (ICMP, TCP, UDP).
Description du champ Match
| Nom du champ | Description |
|---|---|
| in_port | Numéro de port du port de réception |
| in_phy_port | Numéro de port physique du port de réception |
| métadonnées | Métadonnées utilisées pour transmettre des informations entre les tables |
| eth_dst | Adresse MAC de destination d'Ethernet |
| eth_src | Adresse MAC source d'Ethernet |
| eth_type | Type de trame Ethernet |
| vlan_vid | ID VLAN |
| vlan_pcp | VLAN PCP |
| ip_dscp | DSCP IP |
| ip_ecn | IP ECN |
| ip_proto | Type de protocole d'IP |
| ipv4_src | Adresse IP source d'IPv4 |
| ipv4_dst | Adresse IP de destination de IPv4 |
| tcp_src | Numéro de port source de TCP |
| tcp_dst | Numéro de port de destination de TCP |
| udp_src | Numéro de port source d'UDP |
| udp_dst | Numéro de port de destination de UDP |
| sctp_src | Numéro de port source du SCTP |
| sctp_dst | Numéro de port de destination du SCTP |
| icmpv4_type | Type de ICMP |
| icmpv4_code | Code de l'ICMP |
| arp_op | Opcode d'ARP |
| arp_spa | Adresse IP source de l'ARP |
| arp_tpa | Adresse IP cible de l'ARP |
| arp_sha | Adresse MAC source d'ARP |
| arp_tha | Adresse MAC cible de l'ARP |
| ipv6_src | Adresse IP source d'IPv6 |
| ipv6_dst | Adresse IP de destination de IPv6 |
| ipv6_flabel | Étiquette de flux d'IPv6 |
| icmpv6_type | Type d'ICMPv6 |
| icmpv6_code | Code de ICMPv6 |
| ipv6_nd_target | Adresse cible de la découverte du voisin IPv6 |
| ipv6_nd_sll | Adresse de couche liaison source de la découverte de voisin IPv6 |
| ipv6_nd_tll | Adresse de couche liaison cible de la découverte de voisin IPv6 |
| mpls_label | Étiquette MPLS |
| mpls_tc | Classe de trafic MPLS (TC) |
| mpls_bos | MPLS BoS bit |
| pbb_isid | I-SID de 802.1ah PBB |
| tunnel_id | Métadonnées sur le port logique |
| ipv6_exthdr | Pseudo-champ d'en-tête d'extension d'IPv6 |
Dépendances de Match
| Field Name | Field Length | Field Value | Hasmask | Dependencies | Description |
|---|---|---|---|---|---|
| in_port | 32 | 0x00 | 0 | none | Ingress. Représentation numérique du port entrant, commençant à 1. Il peut s'agir d'un port logique physique ou défini par le commutateur. |
| in_phy_port | 32 | 0x01 | 0 | in_port | Physical port. IN ofp_packet_inmessages, annulant le port physique lorsque le paquet est reçu sur un port logique. Métadonnées |
| metadata | 64 | 0x02 | 0 | none | Table Metadata. Utilisé pour transmettre des informations comme étant des tables. |
| 128 | 0x02 | 1 | none | ||
| eth_dst | 48 | 0x03 | 0 | none | Ethernet adresse MAC de destination. |
| 96 | 0x03 | 1 | none | ||
| eth_src | 48 | 0x04 | 0 | none | Ethernet adresse MAC source. |
| 96 | 0x04 | 1 | none | ||
| eth_type | 16 | 0x05 | 0 | none | |
| vlan_vid | 13 | 0x06 | 0 | none | |
| 26 | 0x06 | 1 | none | ||
| vlan_pcp | 3 | 0x07 | 0 | vlan_vid != none | VLAN_PCP à partir de 802.1Q header. |
| ip_dscp | 6 | 0x08 | 0 | eth_type = 0x0800 ou 0x86dd | Diff Code de service. Partie du champ IPv4ToS ou du champ Classe de trafic IPv6. |
| ip_ecn | 2 | 0x09 | 0 | eth_type = 0x0800 ou 0x86dd | bits ECN de l'en-tête IP. Partie du champ IPv4ToS ou du champ de classe de trafic IPv6. |
| ip_proto | 8 | 0x0a | 0 | eth_type = 0x0800 ou 0x86dd | numéro de protocole IPv4 ou IPv6. |
| ipv4_src | 32 | 0x0b | 0 | eth_type = 0x0800 | adresse source IPV4. Peut utiliser un masque de sous réseau ou un bitmask |
| 64 | 0x0b | 1 | eth_type = 0x0800 | ||
| ipv4_dst | 32 | 0x0c | 0 | eth_type = 0x0800 | adresse de destination IPv4. Peut utiliser sous-réseau mask ou un bitmask arbitraire |
| 64 | 0x0c | 1 | eth_type = 0x0800 | ||
| tcp_src | 16 | 0x0d | 0 | ip_proto = 6 | TCP source port |
| tcp_dst | 16 | 0x0e | 0 | ip_proto = 6 | TCP destination port |
| udp_src | 16 | 0x0f | 0 | ip_proto = 17 | UDP Source port |
| udp_dst | 16 | 0x10 | 0 | ip_proto = 17 | UDP destination port |
| sctp_src | 16 | 0x11 | 0 | ip_proto = 132 | SCTP source de port |
| sctp_dst | 16 | 0x12 | 0 | ip_proto = 132 | SCTP destination de port |
| icmpv4_type | 8 | 0x13 | 0 | ip_proto = 1 | ICMP type |
| icmpv4_code | 8 | 0x14 | 0 | ip_proto = 1 | ICMP code |
| arp_op | 16 | 0x15 | 0 | eth_type = 0x0806 | ARP opcode |
| arp_spa | 32 | 0x16 | 0 | eth_type = 0x0806 | adresse IPv4 Source dans la charge utile ARP. On peut utiliser un masque de sous-réseau ou un bitmask arbitraire |
| 64 | 0x16 | 1 | eth_type = 0x0806 | ||
| arp_tpa | 32 | 0x17 | 0 | eth_type = 0x0806 | Target, l'adresse IPv4 dans la charge utile ARP. On peut utiliser un masque de sous-réseau ou un bitmask arbitraire |
| 64 | 0x17 | 1 | eth_type = 0x0806 | ||
| arp_sha | 48 | 0x18 | 0 | eth_type = 0x0806 | adresse Ethernet Source dans la charge ARP. |
| 96 | 0x18 | 1 | eth_type = 0x0806 | ||
| arp_tha | 48 | 0x19 | 0 | eth_type = 0x0806 | Target dans la charge ARP. |
| 96 | 0x19 | 1 | eth_type = 0x0806 | ||
| ipv6_src | 128 | 0x1a | 0 | eth_type = 0x86dd | adresse source IPv6. Peut utiliser sous-réseau mask ou un bitmask arbitraire |
| 256 | 0x1a | 1 | eth_type = 0x86dd | ||
| ipv6_dst | 128 | 0x1b | 0 | eth_type = 0x86dd | adresse destination IPv6. Peut utiliser sous-réseau mask ou un bitmask arbitraire |
| 256 | 0x1b | 1 | eth_type = 0x86dd | ||
| ipv6_flabel | 20 | 0x1c | 0 | eth_type = 0x86dd | label IPv6. |
| 40 | 0x1c | 1 | eth_type = 0x86dd | ||
| icmpv6_type | 8 | 0x1d | 0 | ip_proto = 58 | ICMPv6 type |
| icmpv6_code | 8 | 0x1e | 0 | ip_proto = 58 | ICMPv6 code |
| ipv6_nd_target | 128 | 0x1f | 0 | icmpv6_type = 135 ou 136 | option d'adresse de couche liaison source dans un message de découverte IPv6Neighbor |
| ipv6_nd_sll | 48 | 0x20 | 0 | icmpv6_type = 135 | option d'adresse de couche liaison source dans un message de découverte IPv6Neighbor |
| ipv6_nd_tll | 48 | 0x21 | 0 | icmpv6_type = 136 | option d'adresse de couche liaison cible dans un message de découverte IPv6Neighbor. |
| mpls_label | 20 | 0x22 | 0 | eth_type = 0x8847 ou 0x8848 | LABEL Dans le premier MPLS shim header. |
| mpls_tc | 3 | 0x23 | 0 | eth_type = 0x8847 ou 0x8848 | TC dans le premier MPLS shim header. |
| mpls_bos | 1 | 0x24 | 0 | eth_type = 0x8847 ou 0x8848 | |
| pbb_isid | 24 | 0x25 | 0 | eth_type = 0x88E7 | |
| 48 | 0x25 | 1 | eth_type = 0x88E7 | ||
| tunnel_id | 64 | 0x26 | 0 | none | Metadata associé à un port logique. |
| 128 | 0x26 | 1 | none | ||
| ipv6_hexthdr | 9 | 0x27 | 0 | eth_type = 0x86dd | IPv6 pseudo-champ En-tête d'extension |
| 18 | 0x27 | 1 | eth_type = 0x86dd |
EtherType pour quelques protocoles courants
| Ethertype | Protocole |
|---|---|
| 0x0800 | IP, Internet Protocol. |
| 0x8035 | RARP, Reverse Address Resolution Protocol. |
| 0x814c | SNMP, Simple Network Management Protocol. |
| 0x0806 | ARP, Address Resolution Protocol. |
| 0x8100 | VLAN-tagged frame (IEEE 802.1Q) and Shortest Path Bridging IEEE 802.1aq with NNI compatibility |
| 0x8847 | MPLS, Multi-Protocol Label Switching (unicast). |
| 0x8848 | MPLS, Multi-Protocol Label Switching (multicast). |
| 0x88a8 | Provider Bridging (IEEE 802.1ad) & Shortest Path Bridging IEEE 802.1aq |
| 0x88cc | LLDP, Link Layer Discovery Protocol. |
| 0x86dd | IPv6, Internet Protocol version 6. |
| 0x8999 | BDDP découverte de domaine de diffusion |
L'Objet Actions
Il est important de noter que la prise en charge des actions comme Ethernet, IPv4, TCP et UDP est facultative. Un contrôleur saura uniquement si un commutateur prend en charge ces actions facultatives basées sur un FeatureRes. Action_capabilities indiquera quelles actions sont supportées par un commutateur. Les actions sont installées en tant qu'ensemble d'actions, qui fait partie d'un message FlowMod.
Types d'actions
| Protocole | Comportement | Cible |
|---|---|---|
| Vide | Drop | Jette le paquet |
| Port | Forward | ID de port: transférer vers un port unique |
| All: inonder tous les ports sauf le port d'entrée | ||
| <ID_PORT>: identifiant d'un port | ||
| Controller: envoyé au contrôleur | ||
| Table: paquets injectés par le contrôleur | ||
| Local: pile réseau hôte | ||
| Flood: Inondation non OpenFlow | ||
| Normal: plan de données non OpenFlow | ||
| Queue | Set | ID de file d'attente |
| Ethernet | Set | Source MAC |
| Destination MAC | ||
| VLAN ID | ||
| VLAN Priority | ||
| Strip | VLAN ID | |
| IPv4 | Set | Adresse source |
| Adresse de destination | ||
| Type de service | ||
| TCP | Set | Port source |
| Port de destination | ||
| UDP | Set | Port source |
| Port de destination |
