Dan Talayco | b66b112 | 2010-02-10 22:38:49 -0800 | [diff] [blame] | 1 | """ |
Rich Lane | f688351 | 2013-03-11 17:00:09 -0700 | [diff] [blame] | 2 | Utility parsing functions |
Dan Talayco | b66b112 | 2010-02-10 22:38:49 -0800 | [diff] [blame] | 3 | """ |
| 4 | |
| 5 | import sys |
Dan Talayco | 235d7cb | 2010-03-12 10:03:18 -0800 | [diff] [blame] | 6 | import logging |
Rich Lane | f688351 | 2013-03-11 17:00:09 -0700 | [diff] [blame] | 7 | import of10 as ofp |
Dan Talayco | b66b112 | 2010-02-10 22:38:49 -0800 | [diff] [blame] | 8 | try: |
Dan Talayco | 958f3b9 | 2010-03-12 21:58:57 -0800 | [diff] [blame] | 9 | import scapy.all as scapy |
Dan Talayco | b66b112 | 2010-02-10 22:38:49 -0800 | [diff] [blame] | 10 | except: |
Dan Talayco | d2ca103 | 2010-03-10 14:40:26 -0800 | [diff] [blame] | 11 | try: |
Dan Talayco | 958f3b9 | 2010-03-12 21:58:57 -0800 | [diff] [blame] | 12 | import scapy as scapy |
Dan Talayco | d2ca103 | 2010-03-10 14:40:26 -0800 | [diff] [blame] | 13 | except: |
| 14 | sys.exit("Need to install scapy for packet parsing") |
Dan Talayco | f75360a | 2010-02-05 22:22:54 -0800 | [diff] [blame] | 15 | |
Dan Talayco | b66b112 | 2010-02-10 22:38:49 -0800 | [diff] [blame] | 16 | map_wc_field_to_match_member = { |
Rich Lane | d0478ff | 2013-03-11 12:46:58 -0700 | [diff] [blame] | 17 | 'OFPFW_DL_VLAN' : 'vlan_vid', |
| 18 | 'OFPFW_DL_SRC' : 'eth_src', |
| 19 | 'OFPFW_DL_DST' : 'eth_dst', |
| 20 | 'OFPFW_DL_TYPE' : 'eth_type', |
| 21 | 'OFPFW_NW_PROTO' : 'ip_proto', |
| 22 | 'OFPFW_TP_SRC' : 'tcp_src', |
| 23 | 'OFPFW_TP_DST' : 'tcp_dst', |
Dan Talayco | b66b112 | 2010-02-10 22:38:49 -0800 | [diff] [blame] | 24 | 'OFPFW_NW_SRC_SHIFT' : 'nw_src_shift', |
| 25 | 'OFPFW_NW_SRC_BITS' : 'nw_src_bits', |
| 26 | 'OFPFW_NW_SRC_MASK' : 'nw_src_mask', |
| 27 | 'OFPFW_NW_SRC_ALL' : 'nw_src_all', |
| 28 | 'OFPFW_NW_DST_SHIFT' : 'nw_dst_shift', |
| 29 | 'OFPFW_NW_DST_BITS' : 'nw_dst_bits', |
| 30 | 'OFPFW_NW_DST_MASK' : 'nw_dst_mask', |
| 31 | 'OFPFW_NW_DST_ALL' : 'nw_dst_all', |
Rich Lane | d0478ff | 2013-03-11 12:46:58 -0700 | [diff] [blame] | 32 | 'OFPFW_DL_VLAN_PCP' : 'vlan_pcp', |
| 33 | 'OFPFW_NW_TOS' : 'ip_dscp' |
Dan Talayco | b66b112 | 2010-02-10 22:38:49 -0800 | [diff] [blame] | 34 | } |
| 35 | |
| 36 | |
| 37 | def parse_mac(mac_str): |
Dan Talayco | b9cb548 | 2010-02-09 15:23:12 -0800 | [diff] [blame] | 38 | """ |
Dan Talayco | b66b112 | 2010-02-10 22:38:49 -0800 | [diff] [blame] | 39 | Parse a MAC address |
| 40 | |
| 41 | Parse a MAC address ':' separated string of hex digits to an |
| 42 | array of integer values. '00:d0:05:5d:24:00' => [0, 208, 5, 93, 36, 0] |
| 43 | @param mac_str The string to convert |
| 44 | @return Array of 6 integer values |
| 45 | """ |
Rich Lane | a92f252 | 2012-10-04 18:11:04 -0700 | [diff] [blame] | 46 | return map(lambda val: int(val, 16), mac_str.split(":")) |
Dan Talayco | b66b112 | 2010-02-10 22:38:49 -0800 | [diff] [blame] | 47 | |
| 48 | def parse_ip(ip_str): |
| 49 | """ |
| 50 | Parse an IP address |
| 51 | |
| 52 | Parse an IP address '.' separated string of decimal digits to an |
| 53 | host ordered integer. '172.24.74.77' => |
| 54 | @param ip_str The string to convert |
| 55 | @return Integer value |
| 56 | """ |
Rich Lane | a92f252 | 2012-10-04 18:11:04 -0700 | [diff] [blame] | 57 | array = map(lambda val: int(val), ip_str.split(".")) |
Dan Talayco | b66b112 | 2010-02-10 22:38:49 -0800 | [diff] [blame] | 58 | val = 0 |
| 59 | for a in array: |
| 60 | val <<= 8 |
| 61 | val += a |
| 62 | return val |
| 63 | |
| 64 | def packet_type_classify(ether): |
| 65 | try: |
Dan Talayco | 958f3b9 | 2010-03-12 21:58:57 -0800 | [diff] [blame] | 66 | dot1q = ether[scapy.Dot1Q] |
Dan Talayco | b66b112 | 2010-02-10 22:38:49 -0800 | [diff] [blame] | 67 | except: |
| 68 | dot1q = None |
Dan Talayco | 08d9dfe | 2010-02-12 23:02:11 -0800 | [diff] [blame] | 69 | |
Dan Talayco | b66b112 | 2010-02-10 22:38:49 -0800 | [diff] [blame] | 70 | try: |
Dan Talayco | 958f3b9 | 2010-03-12 21:58:57 -0800 | [diff] [blame] | 71 | ip = ether[scapy.IP] |
Dan Talayco | b66b112 | 2010-02-10 22:38:49 -0800 | [diff] [blame] | 72 | except: |
| 73 | ip = None |
Dan Talayco | 08d9dfe | 2010-02-12 23:02:11 -0800 | [diff] [blame] | 74 | |
Dan Talayco | b66b112 | 2010-02-10 22:38:49 -0800 | [diff] [blame] | 75 | try: |
Dan Talayco | 958f3b9 | 2010-03-12 21:58:57 -0800 | [diff] [blame] | 76 | tcp = ether[scapy.TCP] |
Dan Talayco | b66b112 | 2010-02-10 22:38:49 -0800 | [diff] [blame] | 77 | except: |
| 78 | tcp = None |
Dan Talayco | 08d9dfe | 2010-02-12 23:02:11 -0800 | [diff] [blame] | 79 | |
Dan Talayco | b66b112 | 2010-02-10 22:38:49 -0800 | [diff] [blame] | 80 | try: |
Dan Talayco | 958f3b9 | 2010-03-12 21:58:57 -0800 | [diff] [blame] | 81 | udp = ether[scapy.UDP] |
Dan Talayco | b66b112 | 2010-02-10 22:38:49 -0800 | [diff] [blame] | 82 | except: |
| 83 | udp = None |
| 84 | |
Tatsuya Yabe | b8fb3c3 | 2010-06-14 15:48:36 -0700 | [diff] [blame] | 85 | try: |
| 86 | icmp = ether[scapy.ICMP] |
| 87 | except: |
| 88 | icmp = None |
| 89 | |
Christian Dickmann | 8b59b4b | 2012-09-23 16:48:30 -0700 | [diff] [blame] | 90 | try: |
| 91 | arp = ether[scapy.ARP] |
| 92 | except: |
| 93 | arp = None |
Dan Talayco | b66b112 | 2010-02-10 22:38:49 -0800 | [diff] [blame] | 94 | return (dot1q, ip, tcp, udp, icmp, arp) |
| 95 | |
| 96 | def packet_to_flow_match(packet, pkt_format="L2"): |
| 97 | """ |
| 98 | Create a flow match that matches packet with the given wildcards |
Dan Talayco | b9cb548 | 2010-02-09 15:23:12 -0800 | [diff] [blame] | 99 | |
| 100 | @param packet The packet to use as a flow template |
Dan Talayco | a3b2018 | 2010-02-10 22:49:34 -0800 | [diff] [blame] | 101 | @param pkt_format Currently only L2 is supported. Will indicate the |
| 102 | overall packet type for parsing |
Dan Talayco | b66b112 | 2010-02-10 22:38:49 -0800 | [diff] [blame] | 103 | @return An ofp_match object if successful. None if format is not |
| 104 | recognized. The wildcards of the match will be cleared for the |
| 105 | values extracted from the packet. |
| 106 | |
Dan Talayco | a3b2018 | 2010-02-10 22:49:34 -0800 | [diff] [blame] | 107 | @todo check min length of packet |
| 108 | @todo Check if packet is other than L2 format |
| 109 | @todo Implement ICMP and ARP fields |
Dan Talayco | b9cb548 | 2010-02-09 15:23:12 -0800 | [diff] [blame] | 110 | """ |
| 111 | |
Dan Talayco | b66b112 | 2010-02-10 22:38:49 -0800 | [diff] [blame] | 112 | #@todo check min length of packet |
Dan Talayco | b66b112 | 2010-02-10 22:38:49 -0800 | [diff] [blame] | 113 | if pkt_format.upper() != "L2": |
Rich Lane | f688351 | 2013-03-11 17:00:09 -0700 | [diff] [blame] | 114 | logging.error("Only L2 supported for packet_to_flow") |
Dan Talayco | b66b112 | 2010-02-10 22:38:49 -0800 | [diff] [blame] | 115 | return None |
| 116 | |
Dan Talayco | cb6b5d7 | 2010-03-10 13:59:33 -0800 | [diff] [blame] | 117 | if type(packet) == type(""): |
Dan Talayco | 958f3b9 | 2010-03-12 21:58:57 -0800 | [diff] [blame] | 118 | ether = scapy.Ether(packet) |
Dan Talayco | cb6b5d7 | 2010-03-10 13:59:33 -0800 | [diff] [blame] | 119 | else: |
| 120 | ether = packet |
| 121 | |
Dan Talayco | b66b112 | 2010-02-10 22:38:49 -0800 | [diff] [blame] | 122 | # For now, assume ether IP packet and ignore wildcards |
Dan Talayco | cb6b5d7 | 2010-03-10 13:59:33 -0800 | [diff] [blame] | 123 | try: |
| 124 | (dot1q, ip, tcp, udp, icmp, arp) = packet_type_classify(ether) |
| 125 | except: |
Rich Lane | f688351 | 2013-03-11 17:00:09 -0700 | [diff] [blame] | 126 | logging.error("packet_to_flow_match: Classify error") |
Dan Talayco | cb6b5d7 | 2010-03-10 13:59:33 -0800 | [diff] [blame] | 127 | return None |
Dan Talayco | b66b112 | 2010-02-10 22:38:49 -0800 | [diff] [blame] | 128 | |
Rich Lane | 0237baf | 2013-03-11 22:34:59 -0700 | [diff] [blame] | 129 | match = ofp.match() |
Rich Lane | f688351 | 2013-03-11 17:00:09 -0700 | [diff] [blame] | 130 | match.wildcards = ofp.OFPFW_ALL |
Dan Talayco | b66b112 | 2010-02-10 22:38:49 -0800 | [diff] [blame] | 131 | #@todo Check if packet is other than L2 format |
Rich Lane | d0478ff | 2013-03-11 12:46:58 -0700 | [diff] [blame] | 132 | match.eth_dst = parse_mac(ether.dst) |
Rich Lane | f688351 | 2013-03-11 17:00:09 -0700 | [diff] [blame] | 133 | match.wildcards &= ~ofp.OFPFW_DL_DST |
Rich Lane | d0478ff | 2013-03-11 12:46:58 -0700 | [diff] [blame] | 134 | match.eth_src = parse_mac(ether.src) |
Rich Lane | f688351 | 2013-03-11 17:00:09 -0700 | [diff] [blame] | 135 | match.wildcards &= ~ofp.OFPFW_DL_SRC |
Rich Lane | d0478ff | 2013-03-11 12:46:58 -0700 | [diff] [blame] | 136 | match.eth_type = ether.type |
Rich Lane | f688351 | 2013-03-11 17:00:09 -0700 | [diff] [blame] | 137 | match.wildcards &= ~ofp.OFPFW_DL_TYPE |
Dan Talayco | b66b112 | 2010-02-10 22:38:49 -0800 | [diff] [blame] | 138 | |
| 139 | if dot1q: |
Rich Lane | d0478ff | 2013-03-11 12:46:58 -0700 | [diff] [blame] | 140 | match.vlan_vid = dot1q.vlan |
| 141 | match.vlan_pcp = dot1q.prio |
| 142 | match.eth_type = dot1q.type |
Tatsuya Yabe | b8fb3c3 | 2010-06-14 15:48:36 -0700 | [diff] [blame] | 143 | else: |
Rich Lane | f688351 | 2013-03-11 17:00:09 -0700 | [diff] [blame] | 144 | match.vlan_vid = ofp.OFP_VLAN_NONE |
Rich Lane | d0478ff | 2013-03-11 12:46:58 -0700 | [diff] [blame] | 145 | match.vlan_pcp = 0 |
Rich Lane | f688351 | 2013-03-11 17:00:09 -0700 | [diff] [blame] | 146 | match.wildcards &= ~ofp.OFPFW_DL_VLAN |
| 147 | match.wildcards &= ~ofp.OFPFW_DL_VLAN_PCP |
Dan Talayco | b66b112 | 2010-02-10 22:38:49 -0800 | [diff] [blame] | 148 | |
| 149 | if ip: |
Rich Lane | d0478ff | 2013-03-11 12:46:58 -0700 | [diff] [blame] | 150 | match.ipv4_src = parse_ip(ip.src) |
Rich Lane | f688351 | 2013-03-11 17:00:09 -0700 | [diff] [blame] | 151 | match.wildcards &= ~ofp.OFPFW_NW_SRC_MASK |
Rich Lane | d0478ff | 2013-03-11 12:46:58 -0700 | [diff] [blame] | 152 | match.ipv4_dst = parse_ip(ip.dst) |
Rich Lane | f688351 | 2013-03-11 17:00:09 -0700 | [diff] [blame] | 153 | match.wildcards &= ~ofp.OFPFW_NW_DST_MASK |
Rich Lane | d0478ff | 2013-03-11 12:46:58 -0700 | [diff] [blame] | 154 | match.ip_dscp = ip.tos |
Rich Lane | f688351 | 2013-03-11 17:00:09 -0700 | [diff] [blame] | 155 | match.wildcards &= ~ofp.OFPFW_NW_TOS |
Dan Talayco | b66b112 | 2010-02-10 22:38:49 -0800 | [diff] [blame] | 156 | |
| 157 | if tcp: |
Rich Lane | d0478ff | 2013-03-11 12:46:58 -0700 | [diff] [blame] | 158 | match.ip_proto = 6 |
Rich Lane | f688351 | 2013-03-11 17:00:09 -0700 | [diff] [blame] | 159 | match.wildcards &= ~ofp.OFPFW_NW_PROTO |
Dan Talayco | 89d5734 | 2010-06-07 16:24:59 -0700 | [diff] [blame] | 160 | elif not tcp and udp: |
| 161 | tcp = udp |
Rich Lane | d0478ff | 2013-03-11 12:46:58 -0700 | [diff] [blame] | 162 | match.ip_proto = 17 |
Rich Lane | f688351 | 2013-03-11 17:00:09 -0700 | [diff] [blame] | 163 | match.wildcards &= ~ofp.OFPFW_NW_PROTO |
Dan Talayco | 89d5734 | 2010-06-07 16:24:59 -0700 | [diff] [blame] | 164 | |
| 165 | if tcp: |
Rich Lane | d0478ff | 2013-03-11 12:46:58 -0700 | [diff] [blame] | 166 | match.tcp_src = tcp.sport |
Rich Lane | f688351 | 2013-03-11 17:00:09 -0700 | [diff] [blame] | 167 | match.wildcards &= ~ofp.OFPFW_TP_SRC |
Rich Lane | d0478ff | 2013-03-11 12:46:58 -0700 | [diff] [blame] | 168 | match.tcp_dst = tcp.dport |
Rich Lane | f688351 | 2013-03-11 17:00:09 -0700 | [diff] [blame] | 169 | match.wildcards &= ~ofp.OFPFW_TP_DST |
Dan Talayco | b66b112 | 2010-02-10 22:38:49 -0800 | [diff] [blame] | 170 | |
Tatsuya Yabe | b8fb3c3 | 2010-06-14 15:48:36 -0700 | [diff] [blame] | 171 | if icmp: |
Rich Lane | d0478ff | 2013-03-11 12:46:58 -0700 | [diff] [blame] | 172 | match.ip_proto = 1 |
| 173 | match.tcp_src = icmp.type |
| 174 | match.tcp_dst = icmp.code |
Rich Lane | f688351 | 2013-03-11 17:00:09 -0700 | [diff] [blame] | 175 | match.wildcards &= ~ofp.OFPFW_NW_PROTO |
Tatsuya Yabe | b8fb3c3 | 2010-06-14 15:48:36 -0700 | [diff] [blame] | 176 | |
Christian Dickmann | 8b59b4b | 2012-09-23 16:48:30 -0700 | [diff] [blame] | 177 | if arp: |
Rich Lane | d0478ff | 2013-03-11 12:46:58 -0700 | [diff] [blame] | 178 | match.ip_proto = arp.op |
Rich Lane | f688351 | 2013-03-11 17:00:09 -0700 | [diff] [blame] | 179 | match.wildcards &= ~ofp.OFPFW_NW_PROTO |
Rich Lane | d0478ff | 2013-03-11 12:46:58 -0700 | [diff] [blame] | 180 | match.ipv4_src = parse_ip(arp.psrc) |
Rich Lane | f688351 | 2013-03-11 17:00:09 -0700 | [diff] [blame] | 181 | match.wildcards &= ~ofp.OFPFW_NW_SRC_MASK |
Rich Lane | d0478ff | 2013-03-11 12:46:58 -0700 | [diff] [blame] | 182 | match.ipv4_dst = parse_ip(arp.pdst) |
Rich Lane | f688351 | 2013-03-11 17:00:09 -0700 | [diff] [blame] | 183 | match.wildcards &= ~ofp.OFPFW_NW_DST_MASK |
Dan Talayco | b66b112 | 2010-02-10 22:38:49 -0800 | [diff] [blame] | 184 | |
| 185 | return match |