packets: Make flow_extract() output a pointer to L7 data also.
[sliver-openvswitch.git] / lib / flow.c
1 /* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
2  * Junior University
3  * 
4  * We are making the OpenFlow specification and associated documentation
5  * (Software) available for public use and benefit with the expectation
6  * that others will use, modify and enhance the Software and contribute
7  * those enhancements back to the community. However, since we would
8  * like to make the Software available for broadest use, with as few
9  * restrictions as possible permission is hereby granted, free of
10  * charge, to any person obtaining a copy of this Software to deal in
11  * the Software under the copyrights without restriction, including
12  * without limitation the rights to use, copy, modify, merge, publish,
13  * distribute, sublicense, and/or sell copies of the Software, and to
14  * permit persons to whom the Software is furnished to do so, subject to
15  * the following conditions:
16  * 
17  * The above copyright notice and this permission notice shall be
18  * included in all copies or substantial portions of the Software.
19  * 
20  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23  * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
24  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
25  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
26  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27  * SOFTWARE.
28  * 
29  * The name and trademarks of copyright holder(s) may NOT be used in
30  * advertising or publicity pertaining to the Software or any
31  * derivatives without specific, written prior permission.
32  */
33 #include <sys/types.h>
34 #include "flow.h"
35 #include <inttypes.h>
36 #include <netinet/in.h>
37 #include <string.h>
38 #include "buffer.h"
39 #include "hash.h"
40 #include "openflow.h"
41 #include "packets.h"
42
43 #include "vlog.h"
44 #define THIS_MODULE VLM_flow
45
46 struct ip_header *
47 pull_ip(struct buffer *packet)
48 {
49     if (packet->size >= IP_HEADER_LEN) {
50         struct ip_header *ip = packet->data;
51         int ip_len = IP_IHL(ip->ip_ihl_ver) * 4;
52         if (ip_len >= IP_HEADER_LEN && packet->size >= ip_len) {
53             return buffer_pull(packet, ip_len);
54         }
55     }
56     return NULL;
57 }
58
59 struct tcp_header *
60 pull_tcp(struct buffer *packet) 
61 {
62     if (packet->size >= TCP_HEADER_LEN) {
63         struct tcp_header *tcp = packet->data;
64         int tcp_len = TCP_OFFSET(tcp->tcp_ctl) * 4;
65         if (tcp_len >= TCP_HEADER_LEN && packet->size >= tcp_len) {
66             return buffer_pull(packet, tcp_len);
67         }
68     }
69     return NULL;
70 }
71
72 struct udp_header *
73 pull_udp(struct buffer *packet) 
74 {
75     return buffer_try_pull(packet, UDP_HEADER_LEN);
76 }
77
78 struct eth_header *
79 pull_eth(struct buffer *packet) 
80 {
81     return buffer_try_pull(packet, ETH_HEADER_LEN);
82 }
83
84 void
85 flow_extract(struct buffer *packet, uint16_t in_port, struct flow *flow)
86 {
87     struct buffer b = *packet;
88     struct eth_header *eth;
89
90     if (b.size < ETH_TOTAL_MIN) {
91         /* This message is not too useful since there are various ways that we
92          * can end up with runt frames, e.g. frames that only ever passed
93          * through virtual network devices and never touched a physical
94          * Ethernet. */
95         VLOG_DBG("packet length %zu less than minimum size %d",
96                  b.size, ETH_TOTAL_MIN);
97     }
98
99     memset(flow, 0, sizeof *flow);
100     flow->in_port = htons(in_port);
101
102     packet->l2 = b.data;
103     packet->l3 = NULL;
104     packet->l4 = NULL;
105     packet->l7 = NULL;
106
107     eth = pull_eth(&b);
108     if (eth) {
109         if (ntohs(eth->eth_type) >= OFP_DL_TYPE_ETH2_CUTOFF) {
110             /* This is an Ethernet II frame */
111             flow->dl_type = eth->eth_type;
112         } else {
113             /* This is an 802.2 frame */
114             struct llc_snap_header *h = buffer_at(&b, 0, sizeof *h);
115             if (h == NULL) {
116                 return;
117             }
118             if (h->llc.llc_dsap == LLC_DSAP_SNAP
119                 && h->llc.llc_ssap == LLC_SSAP_SNAP
120                 && h->llc.llc_cntl == LLC_CNTL_SNAP
121                 && !memcmp(h->snap.snap_org, SNAP_ORG_ETHERNET,
122                            sizeof h->snap.snap_org)) {
123                 flow->dl_type = h->snap.snap_type;
124                 buffer_pull(&b, sizeof *h);
125             } else {
126                 flow->dl_type = OFP_DL_TYPE_NOT_ETH_TYPE;
127                 buffer_pull(&b, sizeof(struct llc_header));
128             }
129         }
130
131         /* Check for a VLAN tag */
132         if (flow->dl_type != htons(ETH_TYPE_VLAN)) {
133             flow->dl_vlan = htons(OFP_VLAN_NONE);
134         } else {
135             struct vlan_header *vh = buffer_at(&b, 0, sizeof *vh);
136             flow->dl_type = vh->vlan_next_type;
137             flow->dl_vlan = vh->vlan_tci & htons(VLAN_VID);
138             buffer_pull(&b, sizeof *vh);
139         }
140         memcpy(flow->dl_src, eth->eth_src, ETH_ADDR_LEN);
141         memcpy(flow->dl_dst, eth->eth_dst, ETH_ADDR_LEN);
142
143         packet->l3 = b.data;
144         if (flow->dl_type == htons(ETH_TYPE_IP)) {
145             const struct ip_header *nh = pull_ip(&b);
146             if (nh) {
147                 flow->nw_src = nh->ip_src;
148                 flow->nw_dst = nh->ip_dst;
149                 flow->nw_proto = nh->ip_proto;
150                 packet->l4 = b.data;
151                 if (flow->nw_proto == IP_TYPE_TCP) {
152                     const struct tcp_header *tcp = pull_tcp(&b);
153                     if (tcp) {
154                         flow->tp_src = tcp->tcp_src;
155                         flow->tp_dst = tcp->tcp_dst;
156                         packet->l7 = b.data;
157                     } else {
158                         /* Avoid tricking other code into thinking that this
159                          * packet has an L4 header. */
160                         flow->nw_proto = 0;
161                     }
162                 } else if (flow->nw_proto == IP_TYPE_UDP) {
163                     const struct udp_header *udp = pull_udp(&b);
164                     if (udp) {
165                         flow->tp_src = udp->udp_src;
166                         flow->tp_dst = udp->udp_dst;
167                         packet->l7 = b.data;
168                     } else {
169                         /* Avoid tricking other code into thinking that this
170                          * packet has an L4 header. */
171                         flow->nw_proto = 0;
172                     }
173                 }
174             }
175         }
176     }
177 }
178
179 void
180 flow_print(FILE *stream, const struct flow *flow) 
181 {
182     fprintf(stream,
183             "port%04x:vlan%04x mac"ETH_ADDR_FMT"->"ETH_ADDR_FMT" "
184             "proto%04x ip"IP_FMT"->"IP_FMT" port%d->%d",
185             ntohs(flow->in_port), ntohs(flow->dl_vlan),
186             ETH_ADDR_ARGS(flow->dl_src), ETH_ADDR_ARGS(flow->dl_dst),
187             ntohs(flow->dl_type),
188             IP_ARGS(&flow->nw_src), IP_ARGS(&flow->nw_dst),
189             ntohs(flow->tp_src), ntohs(flow->tp_dst));
190 }
191
192 int
193 flow_compare(const struct flow *a, const struct flow *b)
194 {
195     return memcmp(a, b, sizeof *a);
196 }
197
198 unsigned long int
199 flow_hash(const struct flow *flow, uint32_t basis) 
200 {
201     return hash_fnv(flow, sizeof *flow, basis);
202 }