]>
Commit | Line | Data |
---|---|---|
064af421 | 1 | /* |
3a919aee | 2 | * Copyright (c) 2008, 2009, 2010 Nicira Networks. |
064af421 | 3 | * |
a14bc59f BP |
4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
5 | * you may not use this file except in compliance with the License. | |
6 | * You may obtain a copy of the License at: | |
064af421 | 7 | * |
a14bc59f BP |
8 | * http://www.apache.org/licenses/LICENSE-2.0 |
9 | * | |
10 | * Unless required by applicable law or agreed to in writing, software | |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | * See the License for the specific language governing permissions and | |
14 | * limitations under the License. | |
064af421 BP |
15 | */ |
16 | #include <config.h> | |
17 | #include <sys/types.h> | |
18 | #include "flow.h" | |
19 | #include <inttypes.h> | |
20 | #include <netinet/in.h> | |
21 | #include <stdlib.h> | |
22 | #include <string.h> | |
10a24935 | 23 | #include "byte-order.h" |
064af421 BP |
24 | #include "coverage.h" |
25 | #include "dynamic-string.h" | |
26 | #include "hash.h" | |
27 | #include "ofpbuf.h" | |
28 | #include "openflow/openflow.h" | |
29 | #include "openvswitch/datapath-protocol.h" | |
30 | #include "packets.h" | |
176aaa65 | 31 | #include "unaligned.h" |
5136ce49 | 32 | #include "vlog.h" |
064af421 | 33 | |
d98e6007 | 34 | VLOG_DEFINE_THIS_MODULE(flow); |
064af421 | 35 | |
a26ef517 JP |
36 | static struct arp_eth_header * |
37 | pull_arp(struct ofpbuf *packet) | |
38 | { | |
39 | return ofpbuf_try_pull(packet, ARP_ETH_HEADER_LEN); | |
40 | } | |
41 | ||
064af421 BP |
42 | static struct ip_header * |
43 | pull_ip(struct ofpbuf *packet) | |
44 | { | |
45 | if (packet->size >= IP_HEADER_LEN) { | |
46 | struct ip_header *ip = packet->data; | |
47 | int ip_len = IP_IHL(ip->ip_ihl_ver) * 4; | |
48 | if (ip_len >= IP_HEADER_LEN && packet->size >= ip_len) { | |
49 | return ofpbuf_pull(packet, ip_len); | |
50 | } | |
51 | } | |
52 | return NULL; | |
53 | } | |
54 | ||
55 | static struct tcp_header * | |
d295e8e9 | 56 | pull_tcp(struct ofpbuf *packet) |
064af421 BP |
57 | { |
58 | if (packet->size >= TCP_HEADER_LEN) { | |
59 | struct tcp_header *tcp = packet->data; | |
60 | int tcp_len = TCP_OFFSET(tcp->tcp_ctl) * 4; | |
61 | if (tcp_len >= TCP_HEADER_LEN && packet->size >= tcp_len) { | |
62 | return ofpbuf_pull(packet, tcp_len); | |
63 | } | |
64 | } | |
65 | return NULL; | |
66 | } | |
67 | ||
68 | static struct udp_header * | |
d295e8e9 | 69 | pull_udp(struct ofpbuf *packet) |
064af421 BP |
70 | { |
71 | return ofpbuf_try_pull(packet, UDP_HEADER_LEN); | |
72 | } | |
73 | ||
74 | static struct icmp_header * | |
d295e8e9 | 75 | pull_icmp(struct ofpbuf *packet) |
064af421 BP |
76 | { |
77 | return ofpbuf_try_pull(packet, ICMP_HEADER_LEN); | |
78 | } | |
79 | ||
50f06e16 | 80 | static void |
ae412e7d | 81 | parse_vlan(struct ofpbuf *b, struct flow *flow) |
064af421 | 82 | { |
50f06e16 | 83 | struct qtag_prefix { |
0b3e77bb BP |
84 | ovs_be16 eth_type; /* ETH_TYPE_VLAN */ |
85 | ovs_be16 tci; | |
50f06e16 BP |
86 | }; |
87 | ||
0b3e77bb | 88 | if (b->size >= sizeof(struct qtag_prefix) + sizeof(ovs_be16)) { |
50f06e16 BP |
89 | struct qtag_prefix *qp = ofpbuf_pull(b, sizeof *qp); |
90 | flow->dl_vlan = qp->tci & htons(VLAN_VID_MASK); | |
c2bb4851 | 91 | flow->dl_vlan_pcp = vlan_tci_to_pcp(qp->tci); |
50f06e16 | 92 | } |
064af421 BP |
93 | } |
94 | ||
0b3e77bb | 95 | static ovs_be16 |
50f06e16 | 96 | parse_ethertype(struct ofpbuf *b) |
064af421 | 97 | { |
50f06e16 | 98 | struct llc_snap_header *llc; |
0b3e77bb | 99 | ovs_be16 proto; |
50f06e16 | 100 | |
0b3e77bb | 101 | proto = *(ovs_be16 *) ofpbuf_pull(b, sizeof proto); |
50f06e16 BP |
102 | if (ntohs(proto) >= ODP_DL_TYPE_ETH2_CUTOFF) { |
103 | return proto; | |
104 | } | |
105 | ||
106 | if (b->size < sizeof *llc) { | |
107 | return htons(ODP_DL_TYPE_NOT_ETH_TYPE); | |
108 | } | |
109 | ||
110 | llc = b->data; | |
111 | if (llc->llc.llc_dsap != LLC_DSAP_SNAP | |
112 | || llc->llc.llc_ssap != LLC_SSAP_SNAP | |
113 | || llc->llc.llc_cntl != LLC_CNTL_SNAP | |
114 | || memcmp(llc->snap.snap_org, SNAP_ORG_ETHERNET, | |
115 | sizeof llc->snap.snap_org)) { | |
116 | return htons(ODP_DL_TYPE_NOT_ETH_TYPE); | |
117 | } | |
118 | ||
119 | ofpbuf_pull(b, sizeof *llc); | |
120 | return llc->snap.snap_type; | |
064af421 BP |
121 | } |
122 | ||
0b3e77bb BP |
123 | /* Initializes 'flow' members from 'packet', 'tun_id', and 'in_port. |
124 | * Initializes 'packet' header pointers as follows: | |
ca78c6b6 BP |
125 | * |
126 | * - packet->l2 to the start of the Ethernet header. | |
127 | * | |
128 | * - packet->l3 to just past the Ethernet header, or just past the | |
129 | * vlan_header if one is present, to the first byte of the payload of the | |
130 | * Ethernet frame. | |
131 | * | |
132 | * - packet->l4 to just past the IPv4 header, if one is present and has a | |
133 | * correct length, and otherwise NULL. | |
134 | * | |
135 | * - packet->l7 to just past the TCP or UDP or ICMP header, if one is | |
136 | * present and has a correct length, and otherwise NULL. | |
137 | */ | |
064af421 | 138 | int |
0b3e77bb | 139 | flow_extract(struct ofpbuf *packet, ovs_be32 tun_id, uint16_t in_port, |
ae412e7d | 140 | struct flow *flow) |
064af421 BP |
141 | { |
142 | struct ofpbuf b = *packet; | |
143 | struct eth_header *eth; | |
144 | int retval = 0; | |
145 | ||
146 | COVERAGE_INC(flow_extract); | |
147 | ||
148 | memset(flow, 0, sizeof *flow); | |
659586ef | 149 | flow->tun_id = tun_id; |
064af421 | 150 | flow->in_port = in_port; |
659586ef | 151 | flow->dl_vlan = htons(OFP_VLAN_NONE); |
064af421 BP |
152 | |
153 | packet->l2 = b.data; | |
154 | packet->l3 = NULL; | |
155 | packet->l4 = NULL; | |
156 | packet->l7 = NULL; | |
157 | ||
50f06e16 BP |
158 | if (b.size < sizeof *eth) { |
159 | return 0; | |
160 | } | |
064af421 | 161 | |
50f06e16 BP |
162 | /* Link layer. */ |
163 | eth = b.data; | |
164 | memcpy(flow->dl_src, eth->eth_src, ETH_ADDR_LEN); | |
165 | memcpy(flow->dl_dst, eth->eth_dst, ETH_ADDR_LEN); | |
166 | ||
167 | /* dl_type, dl_vlan, dl_vlan_pcp. */ | |
168 | ofpbuf_pull(&b, ETH_ADDR_LEN * 2); | |
169 | if (eth->eth_type == htons(ETH_TYPE_VLAN)) { | |
170 | parse_vlan(&b, flow); | |
171 | } | |
172 | flow->dl_type = parse_ethertype(&b); | |
173 | ||
174 | /* Network layer. */ | |
175 | packet->l3 = b.data; | |
176 | if (flow->dl_type == htons(ETH_TYPE_IP)) { | |
177 | const struct ip_header *nh = pull_ip(&b); | |
178 | if (nh) { | |
179 | flow->nw_src = get_unaligned_u32(&nh->ip_src); | |
180 | flow->nw_dst = get_unaligned_u32(&nh->ip_dst); | |
181 | flow->nw_tos = nh->ip_tos & IP_DSCP_MASK; | |
182 | flow->nw_proto = nh->ip_proto; | |
183 | packet->l4 = b.data; | |
184 | if (!IP_IS_FRAGMENT(nh->ip_frag_off)) { | |
185 | if (flow->nw_proto == IP_TYPE_TCP) { | |
186 | const struct tcp_header *tcp = pull_tcp(&b); | |
187 | if (tcp) { | |
188 | flow->tp_src = tcp->tcp_src; | |
189 | flow->tp_dst = tcp->tcp_dst; | |
190 | packet->l7 = b.data; | |
50f06e16 BP |
191 | } |
192 | } else if (flow->nw_proto == IP_TYPE_UDP) { | |
193 | const struct udp_header *udp = pull_udp(&b); | |
194 | if (udp) { | |
195 | flow->tp_src = udp->udp_src; | |
196 | flow->tp_dst = udp->udp_dst; | |
197 | packet->l7 = b.data; | |
50f06e16 BP |
198 | } |
199 | } else if (flow->nw_proto == IP_TYPE_ICMP) { | |
200 | const struct icmp_header *icmp = pull_icmp(&b); | |
201 | if (icmp) { | |
202 | flow->icmp_type = htons(icmp->icmp_type); | |
203 | flow->icmp_code = htons(icmp->icmp_code); | |
204 | packet->l7 = b.data; | |
064af421 | 205 | } |
064af421 | 206 | } |
50f06e16 BP |
207 | } else { |
208 | retval = 1; | |
209 | } | |
210 | } | |
211 | } else if (flow->dl_type == htons(ETH_TYPE_ARP)) { | |
212 | const struct arp_eth_header *arp = pull_arp(&b); | |
213 | if (arp && arp->ar_hrd == htons(1) | |
d295e8e9 | 214 | && arp->ar_pro == htons(ETH_TYPE_IP) |
50f06e16 BP |
215 | && arp->ar_hln == ETH_ADDR_LEN |
216 | && arp->ar_pln == 4) { | |
217 | /* We only match on the lower 8 bits of the opcode. */ | |
218 | if (ntohs(arp->ar_op) <= 0xff) { | |
219 | flow->nw_proto = ntohs(arp->ar_op); | |
064af421 | 220 | } |
a26ef517 | 221 | |
d295e8e9 | 222 | if ((flow->nw_proto == ARP_OP_REQUEST) |
50f06e16 BP |
223 | || (flow->nw_proto == ARP_OP_REPLY)) { |
224 | flow->nw_src = arp->ar_spa; | |
225 | flow->nw_dst = arp->ar_tpa; | |
a26ef517 | 226 | } |
064af421 BP |
227 | } |
228 | } | |
229 | return retval; | |
230 | } | |
231 | ||
232 | /* Extracts the flow stats for a packet. The 'flow' and 'packet' | |
233 | * arguments must have been initialized through a call to flow_extract(). | |
234 | */ | |
235 | void | |
ae412e7d | 236 | flow_extract_stats(const struct flow *flow, struct ofpbuf *packet, |
064af421 BP |
237 | struct odp_flow_stats *stats) |
238 | { | |
239 | memset(stats, '\0', sizeof(*stats)); | |
240 | ||
241 | if ((flow->dl_type == htons(ETH_TYPE_IP)) && packet->l4) { | |
064af421 BP |
242 | if ((flow->nw_proto == IP_TYPE_TCP) && packet->l7) { |
243 | struct tcp_header *tcp = packet->l4; | |
244 | stats->tcp_flags = TCP_FLAGS(tcp->tcp_ctl); | |
245 | } | |
246 | } | |
247 | ||
248 | stats->n_bytes = packet->size; | |
249 | stats->n_packets = 1; | |
250 | } | |
251 | ||
fb892732 JP |
252 | /* Extract 'flow' with 'wildcards' into the OpenFlow match structure |
253 | * 'match'. */ | |
064af421 | 254 | void |
ae412e7d BP |
255 | flow_to_match(const struct flow *flow, uint32_t wildcards, |
256 | bool tun_id_from_cookie, struct ofp_match *match) | |
064af421 | 257 | { |
659586ef JG |
258 | if (!tun_id_from_cookie) { |
259 | wildcards &= OFPFW_ALL; | |
260 | } | |
064af421 | 261 | match->wildcards = htonl(wildcards); |
659586ef | 262 | |
064af421 BP |
263 | match->in_port = htons(flow->in_port == ODPP_LOCAL ? OFPP_LOCAL |
264 | : flow->in_port); | |
265 | match->dl_vlan = flow->dl_vlan; | |
959a2ecd | 266 | match->dl_vlan_pcp = flow->dl_vlan_pcp; |
064af421 BP |
267 | memcpy(match->dl_src, flow->dl_src, ETH_ADDR_LEN); |
268 | memcpy(match->dl_dst, flow->dl_dst, ETH_ADDR_LEN); | |
269 | match->dl_type = flow->dl_type; | |
270 | match->nw_src = flow->nw_src; | |
271 | match->nw_dst = flow->nw_dst; | |
834377ea | 272 | match->nw_tos = flow->nw_tos; |
064af421 BP |
273 | match->nw_proto = flow->nw_proto; |
274 | match->tp_src = flow->tp_src; | |
275 | match->tp_dst = flow->tp_dst; | |
959a2ecd JP |
276 | memset(match->pad1, '\0', sizeof match->pad1); |
277 | memset(match->pad2, '\0', sizeof match->pad2); | |
064af421 BP |
278 | } |
279 | ||
280 | void | |
659586ef | 281 | flow_from_match(const struct ofp_match *match, bool tun_id_from_cookie, |
0b3e77bb | 282 | ovs_be64 cookie, struct flow *flow, uint32_t *flow_wildcards) |
064af421 | 283 | { |
2455c7fa | 284 | uint32_t wildcards = ntohl(match->wildcards); |
659586ef | 285 | |
064af421 BP |
286 | flow->nw_src = match->nw_src; |
287 | flow->nw_dst = match->nw_dst; | |
2455c7fa | 288 | if (tun_id_from_cookie && !(wildcards & NXFW_TUN_ID)) { |
659586ef | 289 | flow->tun_id = htonl(ntohll(cookie) >> 32); |
7dab724e | 290 | } else { |
2455c7fa | 291 | wildcards |= NXFW_TUN_ID; |
7dab724e | 292 | flow->tun_id = 0; |
659586ef | 293 | } |
064af421 BP |
294 | flow->in_port = (match->in_port == htons(OFPP_LOCAL) ? ODPP_LOCAL |
295 | : ntohs(match->in_port)); | |
296 | flow->dl_vlan = match->dl_vlan; | |
959a2ecd | 297 | flow->dl_vlan_pcp = match->dl_vlan_pcp; |
064af421 BP |
298 | flow->dl_type = match->dl_type; |
299 | flow->tp_src = match->tp_src; | |
300 | flow->tp_dst = match->tp_dst; | |
301 | memcpy(flow->dl_src, match->dl_src, ETH_ADDR_LEN); | |
302 | memcpy(flow->dl_dst, match->dl_dst, ETH_ADDR_LEN); | |
834377ea | 303 | flow->nw_tos = match->nw_tos; |
064af421 | 304 | flow->nw_proto = match->nw_proto; |
2455c7fa JG |
305 | if (flow_wildcards) { |
306 | *flow_wildcards = wildcards; | |
307 | } | |
064af421 BP |
308 | } |
309 | ||
310 | char * | |
ae412e7d | 311 | flow_to_string(const struct flow *flow) |
064af421 BP |
312 | { |
313 | struct ds ds = DS_EMPTY_INITIALIZER; | |
314 | flow_format(&ds, flow); | |
315 | return ds_cstr(&ds); | |
316 | } | |
317 | ||
318 | void | |
ae412e7d | 319 | flow_format(struct ds *ds, const struct flow *flow) |
064af421 | 320 | { |
659586ef JG |
321 | ds_put_format(ds, "tunnel%08"PRIx32":in_port%04"PRIx16 |
322 | ":vlan%"PRIu16":pcp%"PRIu8 | |
323 | " mac"ETH_ADDR_FMT"->"ETH_ADDR_FMT | |
324 | " type%04"PRIx16 | |
325 | " proto%"PRIu8 | |
326 | " tos%"PRIu8 | |
327 | " ip"IP_FMT"->"IP_FMT | |
328 | " port%"PRIu16"->%"PRIu16, | |
329 | ntohl(flow->tun_id), | |
330 | flow->in_port, | |
331 | ntohs(flow->dl_vlan), | |
332 | flow->dl_vlan_pcp, | |
333 | ETH_ADDR_ARGS(flow->dl_src), | |
334 | ETH_ADDR_ARGS(flow->dl_dst), | |
335 | ntohs(flow->dl_type), | |
336 | flow->nw_proto, | |
337 | flow->nw_tos, | |
338 | IP_ARGS(&flow->nw_src), | |
339 | IP_ARGS(&flow->nw_dst), | |
340 | ntohs(flow->tp_src), | |
341 | ntohs(flow->tp_dst)); | |
064af421 BP |
342 | } |
343 | ||
344 | void | |
ae412e7d | 345 | flow_print(FILE *stream, const struct flow *flow) |
064af421 BP |
346 | { |
347 | char *s = flow_to_string(flow); | |
348 | fputs(s, stream); | |
349 | free(s); | |
350 | } | |
54363004 BP |
351 | \f |
352 | /* flow_wildcards functions. */ | |
353 | ||
354 | /* Given the wildcard bit count in bits 'shift' through 'shift + 5' (inclusive) | |
355 | * of 'wildcards', returns a 32-bit bit mask with a 1 in each bit that must | |
356 | * match and a 0 in each bit that is wildcarded. | |
357 | * | |
358 | * The bits in 'wildcards' are in the format used in enum ofp_flow_wildcards: 0 | |
359 | * is exact match, 1 ignores the LSB, 2 ignores the 2 least-significant bits, | |
360 | * ..., 32 and higher wildcard the entire field. This is the *opposite* of the | |
361 | * usual convention where e.g. /24 indicates that 8 bits (not 24 bits) are | |
362 | * wildcarded. */ | |
363 | ovs_be32 | |
364 | flow_nw_bits_to_mask(uint32_t wildcards, int shift) | |
365 | { | |
366 | wildcards = (wildcards >> shift) & 0x3f; | |
367 | return wildcards < 32 ? htonl(~((1u << wildcards) - 1)) : 0; | |
368 | } | |
369 | ||
370 | void | |
371 | flow_wildcards_init(struct flow_wildcards *wc, uint32_t wildcards) | |
372 | { | |
373 | wc->wildcards = wildcards & OVSFW_ALL; | |
374 | wc->nw_src_mask = flow_nw_bits_to_mask(wc->wildcards, OFPFW_NW_SRC_SHIFT); | |
375 | wc->nw_dst_mask = flow_nw_bits_to_mask(wc->wildcards, OFPFW_NW_DST_SHIFT); | |
376 | } | |
377 |