]>
Commit | Line | Data |
---|---|---|
064af421 | 1 | /* |
8368c090 | 2 | * Copyright (c) 2008, 2009, 2010, 2011 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 | 24 | #include "coverage.h" |
c97fb132 | 25 | #include "dpif.h" |
064af421 BP |
26 | #include "dynamic-string.h" |
27 | #include "hash.h" | |
28 | #include "ofpbuf.h" | |
29 | #include "openflow/openflow.h" | |
30 | #include "openvswitch/datapath-protocol.h" | |
31 | #include "packets.h" | |
176aaa65 | 32 | #include "unaligned.h" |
5136ce49 | 33 | #include "vlog.h" |
064af421 | 34 | |
d98e6007 | 35 | VLOG_DEFINE_THIS_MODULE(flow); |
064af421 | 36 | |
d76f09ea BP |
37 | COVERAGE_DEFINE(flow_extract); |
38 | ||
a26ef517 JP |
39 | static struct arp_eth_header * |
40 | pull_arp(struct ofpbuf *packet) | |
41 | { | |
42 | return ofpbuf_try_pull(packet, ARP_ETH_HEADER_LEN); | |
43 | } | |
44 | ||
064af421 BP |
45 | static struct ip_header * |
46 | pull_ip(struct ofpbuf *packet) | |
47 | { | |
48 | if (packet->size >= IP_HEADER_LEN) { | |
49 | struct ip_header *ip = packet->data; | |
50 | int ip_len = IP_IHL(ip->ip_ihl_ver) * 4; | |
51 | if (ip_len >= IP_HEADER_LEN && packet->size >= ip_len) { | |
52 | return ofpbuf_pull(packet, ip_len); | |
53 | } | |
54 | } | |
55 | return NULL; | |
56 | } | |
57 | ||
58 | static struct tcp_header * | |
d295e8e9 | 59 | pull_tcp(struct ofpbuf *packet) |
064af421 BP |
60 | { |
61 | if (packet->size >= TCP_HEADER_LEN) { | |
62 | struct tcp_header *tcp = packet->data; | |
63 | int tcp_len = TCP_OFFSET(tcp->tcp_ctl) * 4; | |
64 | if (tcp_len >= TCP_HEADER_LEN && packet->size >= tcp_len) { | |
65 | return ofpbuf_pull(packet, tcp_len); | |
66 | } | |
67 | } | |
68 | return NULL; | |
69 | } | |
70 | ||
71 | static struct udp_header * | |
d295e8e9 | 72 | pull_udp(struct ofpbuf *packet) |
064af421 BP |
73 | { |
74 | return ofpbuf_try_pull(packet, UDP_HEADER_LEN); | |
75 | } | |
76 | ||
77 | static struct icmp_header * | |
d295e8e9 | 78 | pull_icmp(struct ofpbuf *packet) |
064af421 BP |
79 | { |
80 | return ofpbuf_try_pull(packet, ICMP_HEADER_LEN); | |
81 | } | |
82 | ||
50f06e16 | 83 | static void |
ae412e7d | 84 | parse_vlan(struct ofpbuf *b, struct flow *flow) |
064af421 | 85 | { |
50f06e16 | 86 | struct qtag_prefix { |
0b3e77bb BP |
87 | ovs_be16 eth_type; /* ETH_TYPE_VLAN */ |
88 | ovs_be16 tci; | |
50f06e16 BP |
89 | }; |
90 | ||
0b3e77bb | 91 | if (b->size >= sizeof(struct qtag_prefix) + sizeof(ovs_be16)) { |
50f06e16 | 92 | struct qtag_prefix *qp = ofpbuf_pull(b, sizeof *qp); |
66642cb4 | 93 | flow->vlan_tci = qp->tci | htons(VLAN_CFI); |
50f06e16 | 94 | } |
064af421 BP |
95 | } |
96 | ||
0b3e77bb | 97 | static ovs_be16 |
50f06e16 | 98 | parse_ethertype(struct ofpbuf *b) |
064af421 | 99 | { |
50f06e16 | 100 | struct llc_snap_header *llc; |
0b3e77bb | 101 | ovs_be16 proto; |
50f06e16 | 102 | |
0b3e77bb | 103 | proto = *(ovs_be16 *) ofpbuf_pull(b, sizeof proto); |
36956a7d | 104 | if (ntohs(proto) >= ETH_TYPE_MIN) { |
50f06e16 BP |
105 | return proto; |
106 | } | |
107 | ||
108 | if (b->size < sizeof *llc) { | |
36956a7d | 109 | return htons(FLOW_DL_TYPE_NONE); |
50f06e16 BP |
110 | } |
111 | ||
112 | llc = b->data; | |
113 | if (llc->llc.llc_dsap != LLC_DSAP_SNAP | |
114 | || llc->llc.llc_ssap != LLC_SSAP_SNAP | |
115 | || llc->llc.llc_cntl != LLC_CNTL_SNAP | |
116 | || memcmp(llc->snap.snap_org, SNAP_ORG_ETHERNET, | |
117 | sizeof llc->snap.snap_org)) { | |
36956a7d | 118 | return htons(FLOW_DL_TYPE_NONE); |
50f06e16 BP |
119 | } |
120 | ||
121 | ofpbuf_pull(b, sizeof *llc); | |
122 | return llc->snap.snap_type; | |
064af421 BP |
123 | } |
124 | ||
0b3e77bb BP |
125 | /* Initializes 'flow' members from 'packet', 'tun_id', and 'in_port. |
126 | * Initializes 'packet' header pointers as follows: | |
ca78c6b6 BP |
127 | * |
128 | * - packet->l2 to the start of the Ethernet header. | |
129 | * | |
130 | * - packet->l3 to just past the Ethernet header, or just past the | |
131 | * vlan_header if one is present, to the first byte of the payload of the | |
132 | * Ethernet frame. | |
133 | * | |
134 | * - packet->l4 to just past the IPv4 header, if one is present and has a | |
135 | * correct length, and otherwise NULL. | |
136 | * | |
137 | * - packet->l7 to just past the TCP or UDP or ICMP header, if one is | |
138 | * present and has a correct length, and otherwise NULL. | |
139 | */ | |
064af421 | 140 | int |
b9298d3f | 141 | flow_extract(struct ofpbuf *packet, ovs_be64 tun_id, uint16_t in_port, |
ae412e7d | 142 | struct flow *flow) |
064af421 BP |
143 | { |
144 | struct ofpbuf b = *packet; | |
145 | struct eth_header *eth; | |
146 | int retval = 0; | |
147 | ||
148 | COVERAGE_INC(flow_extract); | |
149 | ||
150 | memset(flow, 0, sizeof *flow); | |
659586ef | 151 | flow->tun_id = tun_id; |
064af421 BP |
152 | flow->in_port = in_port; |
153 | ||
154 | packet->l2 = b.data; | |
155 | packet->l3 = NULL; | |
156 | packet->l4 = NULL; | |
157 | packet->l7 = NULL; | |
158 | ||
50f06e16 BP |
159 | if (b.size < sizeof *eth) { |
160 | return 0; | |
161 | } | |
064af421 | 162 | |
50f06e16 BP |
163 | /* Link layer. */ |
164 | eth = b.data; | |
165 | memcpy(flow->dl_src, eth->eth_src, ETH_ADDR_LEN); | |
166 | memcpy(flow->dl_dst, eth->eth_dst, ETH_ADDR_LEN); | |
167 | ||
66642cb4 | 168 | /* dl_type, vlan_tci. */ |
50f06e16 BP |
169 | ofpbuf_pull(&b, ETH_ADDR_LEN * 2); |
170 | if (eth->eth_type == htons(ETH_TYPE_VLAN)) { | |
171 | parse_vlan(&b, flow); | |
172 | } | |
173 | flow->dl_type = parse_ethertype(&b); | |
174 | ||
175 | /* Network layer. */ | |
176 | packet->l3 = b.data; | |
177 | if (flow->dl_type == htons(ETH_TYPE_IP)) { | |
178 | const struct ip_header *nh = pull_ip(&b); | |
179 | if (nh) { | |
9ea5d2d5 BP |
180 | flow->nw_src = get_unaligned_be32(&nh->ip_src); |
181 | flow->nw_dst = get_unaligned_be32(&nh->ip_dst); | |
50f06e16 BP |
182 | flow->nw_tos = nh->ip_tos & IP_DSCP_MASK; |
183 | flow->nw_proto = nh->ip_proto; | |
184 | packet->l4 = b.data; | |
185 | if (!IP_IS_FRAGMENT(nh->ip_frag_off)) { | |
186 | if (flow->nw_proto == IP_TYPE_TCP) { | |
187 | const struct tcp_header *tcp = pull_tcp(&b); | |
188 | if (tcp) { | |
189 | flow->tp_src = tcp->tcp_src; | |
190 | flow->tp_dst = tcp->tcp_dst; | |
191 | packet->l7 = b.data; | |
50f06e16 BP |
192 | } |
193 | } else if (flow->nw_proto == IP_TYPE_UDP) { | |
194 | const struct udp_header *udp = pull_udp(&b); | |
195 | if (udp) { | |
196 | flow->tp_src = udp->udp_src; | |
197 | flow->tp_dst = udp->udp_dst; | |
198 | packet->l7 = b.data; | |
50f06e16 BP |
199 | } |
200 | } else if (flow->nw_proto == IP_TYPE_ICMP) { | |
201 | const struct icmp_header *icmp = pull_icmp(&b); | |
202 | if (icmp) { | |
203 | flow->icmp_type = htons(icmp->icmp_type); | |
204 | flow->icmp_code = htons(icmp->icmp_code); | |
205 | packet->l7 = b.data; | |
064af421 | 206 | } |
064af421 | 207 | } |
50f06e16 BP |
208 | } else { |
209 | retval = 1; | |
210 | } | |
211 | } | |
212 | } else if (flow->dl_type == htons(ETH_TYPE_ARP)) { | |
213 | const struct arp_eth_header *arp = pull_arp(&b); | |
214 | if (arp && arp->ar_hrd == htons(1) | |
d295e8e9 | 215 | && arp->ar_pro == htons(ETH_TYPE_IP) |
50f06e16 BP |
216 | && arp->ar_hln == ETH_ADDR_LEN |
217 | && arp->ar_pln == 4) { | |
218 | /* We only match on the lower 8 bits of the opcode. */ | |
219 | if (ntohs(arp->ar_op) <= 0xff) { | |
220 | flow->nw_proto = ntohs(arp->ar_op); | |
064af421 | 221 | } |
a26ef517 | 222 | |
d295e8e9 | 223 | if ((flow->nw_proto == ARP_OP_REQUEST) |
50f06e16 BP |
224 | || (flow->nw_proto == ARP_OP_REPLY)) { |
225 | flow->nw_src = arp->ar_spa; | |
226 | flow->nw_dst = arp->ar_tpa; | |
a26ef517 | 227 | } |
064af421 BP |
228 | } |
229 | } | |
230 | return retval; | |
231 | } | |
232 | ||
233 | /* Extracts the flow stats for a packet. The 'flow' and 'packet' | |
234 | * arguments must have been initialized through a call to flow_extract(). | |
235 | */ | |
236 | void | |
ae412e7d | 237 | flow_extract_stats(const struct flow *flow, struct ofpbuf *packet, |
c97fb132 | 238 | struct dpif_flow_stats *stats) |
064af421 | 239 | { |
c97fb132 | 240 | memset(stats, 0, sizeof(*stats)); |
064af421 BP |
241 | |
242 | if ((flow->dl_type == htons(ETH_TYPE_IP)) && packet->l4) { | |
064af421 BP |
243 | if ((flow->nw_proto == IP_TYPE_TCP) && packet->l7) { |
244 | struct tcp_header *tcp = packet->l4; | |
245 | stats->tcp_flags = TCP_FLAGS(tcp->tcp_ctl); | |
246 | } | |
247 | } | |
248 | ||
249 | stats->n_bytes = packet->size; | |
250 | stats->n_packets = 1; | |
251 | } | |
252 | ||
064af421 | 253 | char * |
ae412e7d | 254 | flow_to_string(const struct flow *flow) |
064af421 BP |
255 | { |
256 | struct ds ds = DS_EMPTY_INITIALIZER; | |
257 | flow_format(&ds, flow); | |
258 | return ds_cstr(&ds); | |
259 | } | |
260 | ||
261 | void | |
ae412e7d | 262 | flow_format(struct ds *ds, const struct flow *flow) |
064af421 | 263 | { |
b9298d3f BP |
264 | ds_put_format(ds, "tunnel%#"PRIx64":in_port%04"PRIx16":tci(", |
265 | flow->tun_id, flow->in_port); | |
66642cb4 BP |
266 | if (flow->vlan_tci) { |
267 | ds_put_format(ds, "vlan%"PRIu16",pcp%d", | |
268 | vlan_tci_to_vid(flow->vlan_tci), | |
269 | vlan_tci_to_pcp(flow->vlan_tci)); | |
270 | } else { | |
271 | ds_put_char(ds, '0'); | |
272 | } | |
273 | ds_put_format(ds, ") mac"ETH_ADDR_FMT"->"ETH_ADDR_FMT | |
659586ef JG |
274 | " type%04"PRIx16 |
275 | " proto%"PRIu8 | |
276 | " tos%"PRIu8 | |
277 | " ip"IP_FMT"->"IP_FMT | |
278 | " port%"PRIu16"->%"PRIu16, | |
659586ef JG |
279 | ETH_ADDR_ARGS(flow->dl_src), |
280 | ETH_ADDR_ARGS(flow->dl_dst), | |
281 | ntohs(flow->dl_type), | |
282 | flow->nw_proto, | |
283 | flow->nw_tos, | |
284 | IP_ARGS(&flow->nw_src), | |
285 | IP_ARGS(&flow->nw_dst), | |
286 | ntohs(flow->tp_src), | |
287 | ntohs(flow->tp_dst)); | |
064af421 BP |
288 | } |
289 | ||
290 | void | |
ae412e7d | 291 | flow_print(FILE *stream, const struct flow *flow) |
064af421 BP |
292 | { |
293 | char *s = flow_to_string(flow); | |
294 | fputs(s, stream); | |
295 | free(s); | |
296 | } | |
54363004 BP |
297 | \f |
298 | /* flow_wildcards functions. */ | |
299 | ||
d8ae4d67 | 300 | /* Initializes 'wc' as a set of wildcards that matches every packet. */ |
54363004 | 301 | void |
d8ae4d67 | 302 | flow_wildcards_init_catchall(struct flow_wildcards *wc) |
54363004 | 303 | { |
d8ae4d67 | 304 | wc->wildcards = FWW_ALL; |
8368c090 | 305 | wc->tun_id_mask = htonll(0); |
d8ae4d67 BP |
306 | wc->nw_src_mask = htonl(0); |
307 | wc->nw_dst_mask = htonl(0); | |
b6c9e612 | 308 | memset(wc->reg_masks, 0, sizeof wc->reg_masks); |
66642cb4 | 309 | wc->vlan_tci_mask = htons(0); |
29b639ed | 310 | wc->zero = 0; |
54363004 BP |
311 | } |
312 | ||
494e43a5 BP |
313 | /* Initializes 'wc' as an exact-match set of wildcards; that is, 'wc' does not |
314 | * wildcard any bits or fields. */ | |
315 | void | |
316 | flow_wildcards_init_exact(struct flow_wildcards *wc) | |
317 | { | |
b6c9e612 | 318 | wc->wildcards = 0; |
8368c090 | 319 | wc->tun_id_mask = htonll(UINT64_MAX); |
b6c9e612 BP |
320 | wc->nw_src_mask = htonl(UINT32_MAX); |
321 | wc->nw_dst_mask = htonl(UINT32_MAX); | |
322 | memset(wc->reg_masks, 0xff, sizeof wc->reg_masks); | |
66642cb4 | 323 | wc->vlan_tci_mask = htons(UINT16_MAX); |
29b639ed | 324 | wc->zero = 0; |
494e43a5 BP |
325 | } |
326 | ||
00561f41 BP |
327 | /* Returns true if 'wc' is exact-match, false if 'wc' wildcards any bits or |
328 | * fields. */ | |
329 | bool | |
330 | flow_wildcards_is_exact(const struct flow_wildcards *wc) | |
331 | { | |
d8ae4d67 | 332 | int i; |
00561f41 | 333 | |
d8ae4d67 | 334 | if (wc->wildcards |
8368c090 | 335 | || wc->tun_id_mask != htonll(UINT64_MAX) |
d8ae4d67 | 336 | || wc->nw_src_mask != htonl(UINT32_MAX) |
66642cb4 BP |
337 | || wc->nw_dst_mask != htonl(UINT32_MAX) |
338 | || wc->vlan_tci_mask != htons(UINT16_MAX)) { | |
d8ae4d67 BP |
339 | return false; |
340 | } | |
341 | ||
342 | for (i = 0; i < FLOW_N_REGS; i++) { | |
343 | if (wc->reg_masks[i] != htonl(UINT32_MAX)) { | |
344 | return false; | |
345 | } | |
346 | } | |
347 | ||
348 | return true; | |
b5d97350 BP |
349 | } |
350 | ||
351 | /* Initializes 'dst' as the combination of wildcards in 'src1' and 'src2'. | |
352 | * That is, a bit or a field is wildcarded in 'dst' if it is wildcarded in | |
353 | * 'src1' or 'src2' or both. */ | |
354 | void | |
355 | flow_wildcards_combine(struct flow_wildcards *dst, | |
356 | const struct flow_wildcards *src1, | |
357 | const struct flow_wildcards *src2) | |
358 | { | |
b6c9e612 | 359 | int i; |
b5d97350 | 360 | |
d8ae4d67 | 361 | dst->wildcards = src1->wildcards | src2->wildcards; |
8368c090 | 362 | dst->tun_id_mask = src1->tun_id_mask & src2->tun_id_mask; |
b5d97350 BP |
363 | dst->nw_src_mask = src1->nw_src_mask & src2->nw_src_mask; |
364 | dst->nw_dst_mask = src1->nw_dst_mask & src2->nw_dst_mask; | |
b6c9e612 BP |
365 | for (i = 0; i < FLOW_N_REGS; i++) { |
366 | dst->reg_masks[i] = src1->reg_masks[i] & src2->reg_masks[i]; | |
367 | } | |
66642cb4 | 368 | dst->vlan_tci_mask = src1->vlan_tci_mask & src2->vlan_tci_mask; |
b5d97350 BP |
369 | } |
370 | ||
371 | /* Returns a hash of the wildcards in 'wc'. */ | |
372 | uint32_t | |
373 | flow_wildcards_hash(const struct flow_wildcards *wc) | |
374 | { | |
d8ae4d67 BP |
375 | /* If you change struct flow_wildcards and thereby trigger this |
376 | * assertion, please check that the new struct flow_wildcards has no holes | |
377 | * in it before you update the assertion. */ | |
8368c090 | 378 | BUILD_ASSERT_DECL(sizeof *wc == 24 + FLOW_N_REGS * 4); |
d8ae4d67 | 379 | return hash_bytes(wc, sizeof *wc, 0); |
b5d97350 BP |
380 | } |
381 | ||
382 | /* Returns true if 'a' and 'b' represent the same wildcards, false if they are | |
383 | * different. */ | |
384 | bool | |
385 | flow_wildcards_equal(const struct flow_wildcards *a, | |
386 | const struct flow_wildcards *b) | |
387 | { | |
b6c9e612 BP |
388 | int i; |
389 | ||
d8ae4d67 | 390 | if (a->wildcards != b->wildcards |
8368c090 | 391 | || a->tun_id_mask != b->tun_id_mask |
d8ae4d67 | 392 | || a->nw_src_mask != b->nw_src_mask |
66642cb4 BP |
393 | || a->nw_dst_mask != b->nw_dst_mask |
394 | || a->vlan_tci_mask != b->vlan_tci_mask) { | |
b6c9e612 BP |
395 | return false; |
396 | } | |
397 | ||
398 | for (i = 0; i < FLOW_N_REGS; i++) { | |
399 | if (a->reg_masks[i] != b->reg_masks[i]) { | |
400 | return false; | |
401 | } | |
402 | } | |
403 | ||
404 | return true; | |
b5d97350 BP |
405 | } |
406 | ||
407 | /* Returns true if at least one bit or field is wildcarded in 'a' but not in | |
408 | * 'b', false otherwise. */ | |
409 | bool | |
410 | flow_wildcards_has_extra(const struct flow_wildcards *a, | |
411 | const struct flow_wildcards *b) | |
412 | { | |
b6c9e612 BP |
413 | int i; |
414 | ||
415 | for (i = 0; i < FLOW_N_REGS; i++) { | |
416 | if ((a->reg_masks[i] & b->reg_masks[i]) != b->reg_masks[i]) { | |
417 | return true; | |
418 | } | |
419 | } | |
420 | ||
d8ae4d67 | 421 | return (a->wildcards & ~b->wildcards |
8368c090 | 422 | || (a->tun_id_mask & b->tun_id_mask) != b->tun_id_mask |
b5d97350 | 423 | || (a->nw_src_mask & b->nw_src_mask) != b->nw_src_mask |
66642cb4 BP |
424 | || (a->nw_dst_mask & b->nw_dst_mask) != b->nw_dst_mask |
425 | || (a->vlan_tci_mask & b->vlan_tci_mask) != b->vlan_tci_mask); | |
b5d97350 BP |
426 | } |
427 | ||
494e43a5 | 428 | static bool |
d8ae4d67 | 429 | set_nw_mask(ovs_be32 *maskp, ovs_be32 mask) |
494e43a5 | 430 | { |
0596e897 | 431 | if (ip_is_cidr(mask)) { |
494e43a5 BP |
432 | *maskp = mask; |
433 | return true; | |
434 | } else { | |
435 | return false; | |
436 | } | |
437 | } | |
438 | ||
439 | /* Sets the IP (or ARP) source wildcard mask to CIDR 'mask' (consisting of N | |
440 | * high-order 1-bit and 32-N low-order 0-bits). Returns true if successful, | |
441 | * false if 'mask' is not a CIDR mask. */ | |
442 | bool | |
443 | flow_wildcards_set_nw_src_mask(struct flow_wildcards *wc, ovs_be32 mask) | |
444 | { | |
d8ae4d67 | 445 | return set_nw_mask(&wc->nw_src_mask, mask); |
494e43a5 BP |
446 | } |
447 | ||
448 | /* Sets the IP (or ARP) destination wildcard mask to CIDR 'mask' (consisting of | |
449 | * N high-order 1-bit and 32-N low-order 0-bits). Returns true if successful, | |
450 | * false if 'mask' is not a CIDR mask. */ | |
451 | bool | |
452 | flow_wildcards_set_nw_dst_mask(struct flow_wildcards *wc, ovs_be32 mask) | |
453 | { | |
d8ae4d67 | 454 | return set_nw_mask(&wc->nw_dst_mask, mask); |
494e43a5 | 455 | } |
b6c9e612 BP |
456 | |
457 | /* Sets the wildcard mask for register 'idx' in 'wc' to 'mask'. | |
458 | * (A 0-bit indicates a wildcard bit.) */ | |
459 | void | |
460 | flow_wildcards_set_reg_mask(struct flow_wildcards *wc, int idx, uint32_t mask) | |
461 | { | |
d8ae4d67 | 462 | wc->reg_masks[idx] = mask; |
b6c9e612 | 463 | } |