]>
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> | |
23 | #include "coverage.h" | |
24 | #include "dynamic-string.h" | |
25 | #include "hash.h" | |
26 | #include "ofpbuf.h" | |
27 | #include "openflow/openflow.h" | |
28 | #include "openvswitch/datapath-protocol.h" | |
29 | #include "packets.h" | |
176aaa65 | 30 | #include "unaligned.h" |
5136ce49 | 31 | #include "vlog.h" |
659586ef | 32 | #include "xtoxll.h" |
064af421 | 33 | |
5136ce49 | 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 * | |
56 | pull_tcp(struct ofpbuf *packet) | |
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 * | |
69 | pull_udp(struct ofpbuf *packet) | |
70 | { | |
71 | return ofpbuf_try_pull(packet, UDP_HEADER_LEN); | |
72 | } | |
73 | ||
74 | static struct icmp_header * | |
75 | pull_icmp(struct ofpbuf *packet) | |
76 | { | |
77 | return ofpbuf_try_pull(packet, ICMP_HEADER_LEN); | |
78 | } | |
79 | ||
80 | static struct eth_header * | |
81 | pull_eth(struct ofpbuf *packet) | |
82 | { | |
83 | return ofpbuf_try_pull(packet, ETH_HEADER_LEN); | |
84 | } | |
85 | ||
86 | static struct vlan_header * | |
87 | pull_vlan(struct ofpbuf *packet) | |
88 | { | |
89 | return ofpbuf_try_pull(packet, VLAN_HEADER_LEN); | |
90 | } | |
91 | ||
659586ef JG |
92 | /* Returns 1 if 'packet' is an IP fragment, 0 otherwise. |
93 | * 'tun_id' is in network byte order, while 'in_port' is in host byte order. | |
94 | * These byte orders are the same as they are in struct odp_flow_key. */ | |
064af421 | 95 | int |
659586ef JG |
96 | flow_extract(struct ofpbuf *packet, uint32_t tun_id, uint16_t in_port, |
97 | flow_t *flow) | |
064af421 BP |
98 | { |
99 | struct ofpbuf b = *packet; | |
100 | struct eth_header *eth; | |
101 | int retval = 0; | |
102 | ||
103 | COVERAGE_INC(flow_extract); | |
104 | ||
105 | memset(flow, 0, sizeof *flow); | |
659586ef | 106 | flow->tun_id = tun_id; |
064af421 | 107 | flow->in_port = in_port; |
659586ef | 108 | flow->dl_vlan = htons(OFP_VLAN_NONE); |
064af421 BP |
109 | |
110 | packet->l2 = b.data; | |
111 | packet->l3 = NULL; | |
112 | packet->l4 = NULL; | |
113 | packet->l7 = NULL; | |
114 | ||
115 | eth = pull_eth(&b); | |
116 | if (eth) { | |
117 | if (ntohs(eth->eth_type) >= OFP_DL_TYPE_ETH2_CUTOFF) { | |
118 | /* This is an Ethernet II frame */ | |
119 | flow->dl_type = eth->eth_type; | |
120 | } else { | |
121 | /* This is an 802.2 frame */ | |
122 | struct llc_header *llc = ofpbuf_at(&b, 0, sizeof *llc); | |
123 | struct snap_header *snap = ofpbuf_at(&b, sizeof *llc, | |
124 | sizeof *snap); | |
125 | if (llc == NULL) { | |
126 | return 0; | |
127 | } | |
128 | if (snap | |
129 | && llc->llc_dsap == LLC_DSAP_SNAP | |
130 | && llc->llc_ssap == LLC_SSAP_SNAP | |
131 | && llc->llc_cntl == LLC_CNTL_SNAP | |
132 | && !memcmp(snap->snap_org, SNAP_ORG_ETHERNET, | |
133 | sizeof snap->snap_org)) { | |
134 | flow->dl_type = snap->snap_type; | |
135 | ofpbuf_pull(&b, LLC_SNAP_HEADER_LEN); | |
136 | } else { | |
137 | flow->dl_type = htons(OFP_DL_TYPE_NOT_ETH_TYPE); | |
138 | ofpbuf_pull(&b, sizeof(struct llc_header)); | |
139 | } | |
140 | } | |
141 | ||
142 | /* Check for a VLAN tag */ | |
143 | if (flow->dl_type == htons(ETH_TYPE_VLAN)) { | |
144 | struct vlan_header *vh = pull_vlan(&b); | |
145 | if (vh) { | |
146 | flow->dl_type = vh->vlan_next_type; | |
147 | flow->dl_vlan = vh->vlan_tci & htons(VLAN_VID_MASK); | |
959a2ecd | 148 | flow->dl_vlan_pcp = (ntohs(vh->vlan_tci) & 0xe000) >> 13; |
064af421 BP |
149 | } |
150 | } | |
151 | memcpy(flow->dl_src, eth->eth_src, ETH_ADDR_LEN); | |
152 | memcpy(flow->dl_dst, eth->eth_dst, ETH_ADDR_LEN); | |
153 | ||
154 | packet->l3 = b.data; | |
155 | if (flow->dl_type == htons(ETH_TYPE_IP)) { | |
156 | const struct ip_header *nh = pull_ip(&b); | |
157 | if (nh) { | |
176aaa65 BP |
158 | flow->nw_src = get_unaligned_u32(&nh->ip_src); |
159 | flow->nw_dst = get_unaligned_u32(&nh->ip_dst); | |
f1193301 | 160 | flow->nw_tos = nh->ip_tos & IP_DSCP_MASK; |
064af421 BP |
161 | flow->nw_proto = nh->ip_proto; |
162 | packet->l4 = b.data; | |
163 | if (!IP_IS_FRAGMENT(nh->ip_frag_off)) { | |
164 | if (flow->nw_proto == IP_TYPE_TCP) { | |
165 | const struct tcp_header *tcp = pull_tcp(&b); | |
166 | if (tcp) { | |
167 | flow->tp_src = tcp->tcp_src; | |
168 | flow->tp_dst = tcp->tcp_dst; | |
169 | packet->l7 = b.data; | |
170 | } else { | |
171 | /* Avoid tricking other code into thinking that | |
172 | * this packet has an L4 header. */ | |
173 | flow->nw_proto = 0; | |
174 | } | |
175 | } else if (flow->nw_proto == IP_TYPE_UDP) { | |
176 | const struct udp_header *udp = pull_udp(&b); | |
177 | if (udp) { | |
178 | flow->tp_src = udp->udp_src; | |
179 | flow->tp_dst = udp->udp_dst; | |
180 | packet->l7 = b.data; | |
181 | } else { | |
182 | /* Avoid tricking other code into thinking that | |
183 | * this packet has an L4 header. */ | |
184 | flow->nw_proto = 0; | |
185 | } | |
186 | } else if (flow->nw_proto == IP_TYPE_ICMP) { | |
187 | const struct icmp_header *icmp = pull_icmp(&b); | |
188 | if (icmp) { | |
189 | flow->icmp_type = htons(icmp->icmp_type); | |
190 | flow->icmp_code = htons(icmp->icmp_code); | |
191 | packet->l7 = b.data; | |
192 | } else { | |
193 | /* Avoid tricking other code into thinking that | |
194 | * this packet has an L4 header. */ | |
195 | flow->nw_proto = 0; | |
196 | } | |
197 | } | |
198 | } else { | |
199 | retval = 1; | |
200 | } | |
201 | } | |
a26ef517 JP |
202 | } else if (flow->dl_type == htons(ETH_TYPE_ARP)) { |
203 | const struct arp_eth_header *arp = pull_arp(&b); | |
204 | if (arp && arp->ar_hrd == htons(1) | |
205 | && arp->ar_pro == htons(ETH_TYPE_IP) | |
206 | && arp->ar_hln == ETH_ADDR_LEN | |
207 | && arp->ar_pln == 4) { | |
208 | /* We only match on the lower 8 bits of the opcode. */ | |
209 | if (ntohs(arp->ar_op) <= 0xff) { | |
210 | flow->nw_proto = ntohs(arp->ar_op); | |
211 | } | |
212 | ||
213 | if ((flow->nw_proto == ARP_OP_REQUEST) | |
214 | || (flow->nw_proto == ARP_OP_REPLY)) { | |
215 | flow->nw_src = arp->ar_spa; | |
216 | flow->nw_dst = arp->ar_tpa; | |
217 | } | |
218 | } | |
064af421 BP |
219 | } |
220 | } | |
221 | return retval; | |
222 | } | |
223 | ||
224 | /* Extracts the flow stats for a packet. The 'flow' and 'packet' | |
225 | * arguments must have been initialized through a call to flow_extract(). | |
226 | */ | |
227 | void | |
228 | flow_extract_stats(const flow_t *flow, struct ofpbuf *packet, | |
229 | struct odp_flow_stats *stats) | |
230 | { | |
231 | memset(stats, '\0', sizeof(*stats)); | |
232 | ||
233 | if ((flow->dl_type == htons(ETH_TYPE_IP)) && packet->l4) { | |
064af421 BP |
234 | if ((flow->nw_proto == IP_TYPE_TCP) && packet->l7) { |
235 | struct tcp_header *tcp = packet->l4; | |
236 | stats->tcp_flags = TCP_FLAGS(tcp->tcp_ctl); | |
237 | } | |
238 | } | |
239 | ||
240 | stats->n_bytes = packet->size; | |
241 | stats->n_packets = 1; | |
242 | } | |
243 | ||
fb892732 JP |
244 | /* Extract 'flow' with 'wildcards' into the OpenFlow match structure |
245 | * 'match'. */ | |
064af421 | 246 | void |
659586ef JG |
247 | flow_to_match(const flow_t *flow, uint32_t wildcards, bool tun_id_from_cookie, |
248 | struct ofp_match *match) | |
064af421 | 249 | { |
659586ef JG |
250 | if (!tun_id_from_cookie) { |
251 | wildcards &= OFPFW_ALL; | |
252 | } | |
064af421 | 253 | match->wildcards = htonl(wildcards); |
659586ef | 254 | |
064af421 BP |
255 | match->in_port = htons(flow->in_port == ODPP_LOCAL ? OFPP_LOCAL |
256 | : flow->in_port); | |
257 | match->dl_vlan = flow->dl_vlan; | |
959a2ecd | 258 | match->dl_vlan_pcp = flow->dl_vlan_pcp; |
064af421 BP |
259 | memcpy(match->dl_src, flow->dl_src, ETH_ADDR_LEN); |
260 | memcpy(match->dl_dst, flow->dl_dst, ETH_ADDR_LEN); | |
261 | match->dl_type = flow->dl_type; | |
262 | match->nw_src = flow->nw_src; | |
263 | match->nw_dst = flow->nw_dst; | |
834377ea | 264 | match->nw_tos = flow->nw_tos; |
064af421 BP |
265 | match->nw_proto = flow->nw_proto; |
266 | match->tp_src = flow->tp_src; | |
267 | match->tp_dst = flow->tp_dst; | |
959a2ecd JP |
268 | memset(match->pad1, '\0', sizeof match->pad1); |
269 | memset(match->pad2, '\0', sizeof match->pad2); | |
064af421 BP |
270 | } |
271 | ||
272 | void | |
659586ef | 273 | flow_from_match(const struct ofp_match *match, bool tun_id_from_cookie, |
2455c7fa | 274 | uint64_t cookie, flow_t *flow, uint32_t *flow_wildcards) |
064af421 | 275 | { |
2455c7fa | 276 | uint32_t wildcards = ntohl(match->wildcards); |
659586ef | 277 | |
064af421 BP |
278 | flow->nw_src = match->nw_src; |
279 | flow->nw_dst = match->nw_dst; | |
2455c7fa | 280 | if (tun_id_from_cookie && !(wildcards & NXFW_TUN_ID)) { |
659586ef | 281 | flow->tun_id = htonl(ntohll(cookie) >> 32); |
7dab724e | 282 | } else { |
2455c7fa | 283 | wildcards |= NXFW_TUN_ID; |
7dab724e | 284 | flow->tun_id = 0; |
659586ef | 285 | } |
064af421 BP |
286 | flow->in_port = (match->in_port == htons(OFPP_LOCAL) ? ODPP_LOCAL |
287 | : ntohs(match->in_port)); | |
288 | flow->dl_vlan = match->dl_vlan; | |
959a2ecd | 289 | flow->dl_vlan_pcp = match->dl_vlan_pcp; |
064af421 BP |
290 | flow->dl_type = match->dl_type; |
291 | flow->tp_src = match->tp_src; | |
292 | flow->tp_dst = match->tp_dst; | |
293 | memcpy(flow->dl_src, match->dl_src, ETH_ADDR_LEN); | |
294 | memcpy(flow->dl_dst, match->dl_dst, ETH_ADDR_LEN); | |
834377ea | 295 | flow->nw_tos = match->nw_tos; |
064af421 | 296 | flow->nw_proto = match->nw_proto; |
834377ea | 297 | memset(flow->reserved, 0, sizeof flow->reserved); |
2455c7fa JG |
298 | |
299 | if (flow_wildcards) { | |
300 | *flow_wildcards = wildcards; | |
301 | } | |
064af421 BP |
302 | } |
303 | ||
304 | char * | |
305 | flow_to_string(const flow_t *flow) | |
306 | { | |
307 | struct ds ds = DS_EMPTY_INITIALIZER; | |
308 | flow_format(&ds, flow); | |
309 | return ds_cstr(&ds); | |
310 | } | |
311 | ||
312 | void | |
313 | flow_format(struct ds *ds, const flow_t *flow) | |
314 | { | |
659586ef JG |
315 | ds_put_format(ds, "tunnel%08"PRIx32":in_port%04"PRIx16 |
316 | ":vlan%"PRIu16":pcp%"PRIu8 | |
317 | " mac"ETH_ADDR_FMT"->"ETH_ADDR_FMT | |
318 | " type%04"PRIx16 | |
319 | " proto%"PRIu8 | |
320 | " tos%"PRIu8 | |
321 | " ip"IP_FMT"->"IP_FMT | |
322 | " port%"PRIu16"->%"PRIu16, | |
323 | ntohl(flow->tun_id), | |
324 | flow->in_port, | |
325 | ntohs(flow->dl_vlan), | |
326 | flow->dl_vlan_pcp, | |
327 | ETH_ADDR_ARGS(flow->dl_src), | |
328 | ETH_ADDR_ARGS(flow->dl_dst), | |
329 | ntohs(flow->dl_type), | |
330 | flow->nw_proto, | |
331 | flow->nw_tos, | |
332 | IP_ARGS(&flow->nw_src), | |
333 | IP_ARGS(&flow->nw_dst), | |
334 | ntohs(flow->tp_src), | |
335 | ntohs(flow->tp_dst)); | |
064af421 BP |
336 | } |
337 | ||
338 | void | |
339 | flow_print(FILE *stream, const flow_t *flow) | |
340 | { | |
341 | char *s = flow_to_string(flow); | |
342 | fputs(s, stream); | |
343 | free(s); | |
344 | } |