]>
git.proxmox.com Git - mirror_ovs.git/blob - lib/flow.c
2 * Copyright (c) 2008, 2009, 2010 Nicira Networks.
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:
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 #include <sys/types.h>
20 #include <netinet/in.h>
24 #include "dynamic-string.h"
27 #include "openflow/openflow.h"
28 #include "openvswitch/datapath-protocol.h"
30 #include "unaligned.h"
34 VLOG_DEFINE_THIS_MODULE(flow
)
36 static struct arp_eth_header
*
37 pull_arp(struct ofpbuf
*packet
)
39 return ofpbuf_try_pull(packet
, ARP_ETH_HEADER_LEN
);
42 static struct ip_header
*
43 pull_ip(struct ofpbuf
*packet
)
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
);
55 static struct tcp_header
*
56 pull_tcp(struct ofpbuf
*packet
)
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
);
68 static struct udp_header
*
69 pull_udp(struct ofpbuf
*packet
)
71 return ofpbuf_try_pull(packet
, UDP_HEADER_LEN
);
74 static struct icmp_header
*
75 pull_icmp(struct ofpbuf
*packet
)
77 return ofpbuf_try_pull(packet
, ICMP_HEADER_LEN
);
80 static struct eth_header
*
81 pull_eth(struct ofpbuf
*packet
)
83 return ofpbuf_try_pull(packet
, ETH_HEADER_LEN
);
86 static struct vlan_header
*
87 pull_vlan(struct ofpbuf
*packet
)
89 return ofpbuf_try_pull(packet
, VLAN_HEADER_LEN
);
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. */
96 flow_extract(struct ofpbuf
*packet
, uint32_t tun_id
, uint16_t in_port
,
99 struct ofpbuf b
= *packet
;
100 struct eth_header
*eth
;
103 COVERAGE_INC(flow_extract
);
105 memset(flow
, 0, sizeof *flow
);
106 flow
->tun_id
= tun_id
;
107 flow
->in_port
= in_port
;
108 flow
->dl_vlan
= htons(OFP_VLAN_NONE
);
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
;
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
,
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
);
137 flow
->dl_type
= htons(OFP_DL_TYPE_NOT_ETH_TYPE
);
138 ofpbuf_pull(&b
, sizeof(struct llc_header
));
142 /* Check for a VLAN tag */
143 if (flow
->dl_type
== htons(ETH_TYPE_VLAN
)) {
144 struct vlan_header
*vh
= pull_vlan(&b
);
146 flow
->dl_type
= vh
->vlan_next_type
;
147 flow
->dl_vlan
= vh
->vlan_tci
& htons(VLAN_VID_MASK
);
148 flow
->dl_vlan_pcp
= (ntohs(vh
->vlan_tci
) & 0xe000) >> 13;
151 memcpy(flow
->dl_src
, eth
->eth_src
, ETH_ADDR_LEN
);
152 memcpy(flow
->dl_dst
, eth
->eth_dst
, ETH_ADDR_LEN
);
155 if (flow
->dl_type
== htons(ETH_TYPE_IP
)) {
156 const struct ip_header
*nh
= pull_ip(&b
);
158 flow
->nw_src
= get_unaligned_u32(&nh
->ip_src
);
159 flow
->nw_dst
= get_unaligned_u32(&nh
->ip_dst
);
160 flow
->nw_tos
= nh
->ip_tos
& IP_DSCP_MASK
;
161 flow
->nw_proto
= nh
->ip_proto
;
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
);
167 flow
->tp_src
= tcp
->tcp_src
;
168 flow
->tp_dst
= tcp
->tcp_dst
;
171 /* Avoid tricking other code into thinking that
172 * this packet has an L4 header. */
175 } else if (flow
->nw_proto
== IP_TYPE_UDP
) {
176 const struct udp_header
*udp
= pull_udp(&b
);
178 flow
->tp_src
= udp
->udp_src
;
179 flow
->tp_dst
= udp
->udp_dst
;
182 /* Avoid tricking other code into thinking that
183 * this packet has an L4 header. */
186 } else if (flow
->nw_proto
== IP_TYPE_ICMP
) {
187 const struct icmp_header
*icmp
= pull_icmp(&b
);
189 flow
->icmp_type
= htons(icmp
->icmp_type
);
190 flow
->icmp_code
= htons(icmp
->icmp_code
);
193 /* Avoid tricking other code into thinking that
194 * this packet has an L4 header. */
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
);
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
;
224 /* Extracts the flow stats for a packet. The 'flow' and 'packet'
225 * arguments must have been initialized through a call to flow_extract().
228 flow_extract_stats(const flow_t
*flow
, struct ofpbuf
*packet
,
229 struct odp_flow_stats
*stats
)
231 memset(stats
, '\0', sizeof(*stats
));
233 if ((flow
->dl_type
== htons(ETH_TYPE_IP
)) && packet
->l4
) {
234 struct ip_header
*ip
= packet
->l3
;
235 stats
->ip_tos
= ip
->ip_tos
;
236 if ((flow
->nw_proto
== IP_TYPE_TCP
) && packet
->l7
) {
237 struct tcp_header
*tcp
= packet
->l4
;
238 stats
->tcp_flags
= TCP_FLAGS(tcp
->tcp_ctl
);
242 stats
->n_bytes
= packet
->size
;
243 stats
->n_packets
= 1;
246 /* Extract 'flow' with 'wildcards' into the OpenFlow match structure
249 flow_to_match(const flow_t
*flow
, uint32_t wildcards
, bool tun_id_from_cookie
,
250 struct ofp_match
*match
)
252 if (!tun_id_from_cookie
) {
253 wildcards
&= OFPFW_ALL
;
255 match
->wildcards
= htonl(wildcards
);
257 match
->in_port
= htons(flow
->in_port
== ODPP_LOCAL
? OFPP_LOCAL
259 match
->dl_vlan
= flow
->dl_vlan
;
260 match
->dl_vlan_pcp
= flow
->dl_vlan_pcp
;
261 memcpy(match
->dl_src
, flow
->dl_src
, ETH_ADDR_LEN
);
262 memcpy(match
->dl_dst
, flow
->dl_dst
, ETH_ADDR_LEN
);
263 match
->dl_type
= flow
->dl_type
;
264 match
->nw_src
= flow
->nw_src
;
265 match
->nw_dst
= flow
->nw_dst
;
266 match
->nw_tos
= flow
->nw_tos
;
267 match
->nw_proto
= flow
->nw_proto
;
268 match
->tp_src
= flow
->tp_src
;
269 match
->tp_dst
= flow
->tp_dst
;
270 memset(match
->pad1
, '\0', sizeof match
->pad1
);
271 memset(match
->pad2
, '\0', sizeof match
->pad2
);
275 flow_from_match(const struct ofp_match
*match
, bool tun_id_from_cookie
,
276 uint64_t cookie
, flow_t
*flow
, uint32_t *flow_wildcards
)
278 uint32_t wildcards
= ntohl(match
->wildcards
);
280 flow
->nw_src
= match
->nw_src
;
281 flow
->nw_dst
= match
->nw_dst
;
282 if (tun_id_from_cookie
&& !(wildcards
& NXFW_TUN_ID
)) {
283 flow
->tun_id
= htonl(ntohll(cookie
) >> 32);
285 wildcards
|= NXFW_TUN_ID
;
288 flow
->in_port
= (match
->in_port
== htons(OFPP_LOCAL
) ? ODPP_LOCAL
289 : ntohs(match
->in_port
));
290 flow
->dl_vlan
= match
->dl_vlan
;
291 flow
->dl_vlan_pcp
= match
->dl_vlan_pcp
;
292 flow
->dl_type
= match
->dl_type
;
293 flow
->tp_src
= match
->tp_src
;
294 flow
->tp_dst
= match
->tp_dst
;
295 memcpy(flow
->dl_src
, match
->dl_src
, ETH_ADDR_LEN
);
296 memcpy(flow
->dl_dst
, match
->dl_dst
, ETH_ADDR_LEN
);
297 flow
->nw_tos
= match
->nw_tos
;
298 flow
->nw_proto
= match
->nw_proto
;
299 memset(flow
->reserved
, 0, sizeof flow
->reserved
);
301 if (flow_wildcards
) {
302 *flow_wildcards
= wildcards
;
307 flow_to_string(const flow_t
*flow
)
309 struct ds ds
= DS_EMPTY_INITIALIZER
;
310 flow_format(&ds
, flow
);
315 flow_format(struct ds
*ds
, const flow_t
*flow
)
317 ds_put_format(ds
, "tunnel%08"PRIx32
":in_port%04"PRIx16
318 ":vlan%"PRIu16
":pcp%"PRIu8
319 " mac"ETH_ADDR_FMT
"->"ETH_ADDR_FMT
323 " ip"IP_FMT
"->"IP_FMT
324 " port%"PRIu16
"->%"PRIu16
,
327 ntohs(flow
->dl_vlan
),
329 ETH_ADDR_ARGS(flow
->dl_src
),
330 ETH_ADDR_ARGS(flow
->dl_dst
),
331 ntohs(flow
->dl_type
),
334 IP_ARGS(&flow
->nw_src
),
335 IP_ARGS(&flow
->nw_dst
),
337 ntohs(flow
->tp_dst
));
341 flow_print(FILE *stream
, const flow_t
*flow
)
343 char *s
= flow_to_string(flow
);