For SNAT, don't store the pre-fragment L2 header before actions are applied.
[sliver-openvswitch.git] / secchan / flow-end.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
34 #include <config.h>
35 #include "flow-end.h"
36 #include <errno.h>
37 #include <arpa/inet.h>
38 #include <inttypes.h>
39 #include <stdlib.h>
40 #include <unistd.h>
41 #include <sys/time.h>
42 #include <time.h>
43 #include "openflow/nicira-ext.h"
44 #include "openflow/openflow.h"
45 #include "secchan.h"
46 #include "ofpbuf.h"
47 #include "vconn.h"
48 #include "rconn.h"
49 #include "socket-util.h"
50 #include "xtoxll.h"
51 #include "netflow.h"
52
53 #define THIS_MODULE VLM_flow_end
54 #include "vlog.h"
55
56 struct flow_end_data {
57     struct rconn *remote_rconn;
58     struct rconn *local_rconn;
59
60     bool send_ofp_exp;         /* Send OpenFlow 'flow expired' messages? */
61
62     int netflow_fd;            /* Socket for NetFlow collector. */
63     uint32_t netflow_cnt;      /* Flow sequence number for NetFlow. */
64 };
65
66 static int
67 udp_open(char *dst)
68 {
69     char *save_ptr;
70     const char *host_name;
71     const char *port_string;
72     struct sockaddr_in sin;
73     int retval;
74     int fd;
75
76     /* Glibc 2.7 has a bug in strtok_r when compiling with optimization that
77      * can cause segfaults here:
78      * http://sources.redhat.com/bugzilla/show_bug.cgi?id=5614.
79      * Using "::" instead of the obvious ":" works around it. */
80     host_name = strtok_r(dst, "::", &save_ptr);
81     port_string = strtok_r(NULL, "::", &save_ptr);
82     if (!host_name) {
83         ofp_error(0, "%s: bad peer name format", dst);
84         return -EAFNOSUPPORT;
85     }
86     if (!port_string) {
87         ofp_error(0, "%s: bad port format", dst);
88         return -EAFNOSUPPORT;
89     }
90
91     memset(&sin, 0, sizeof sin);
92     sin.sin_family = AF_INET;
93     if (lookup_ip(host_name, &sin.sin_addr)) {
94         return -ENOENT;
95     }
96     sin.sin_port = htons(atoi(port_string));
97
98     fd = socket(AF_INET, SOCK_DGRAM, 0);
99     if (fd < 0) {
100         VLOG_ERR("%s: socket: %s", dst, strerror(errno));
101         return -errno;
102     }
103
104     retval = set_nonblocking(fd);
105     if (retval) {
106         close(fd);
107         return -retval;
108     }
109
110     retval = connect(fd, (struct sockaddr *) &sin, sizeof sin);
111     if (retval < 0) {
112         int error = errno;
113         VLOG_ERR("%s: connect: %s", dst, strerror(error));
114         close(fd);
115         return -error;
116     }
117
118     return fd;
119 }
120
121 static void
122 send_netflow_msg(const struct nx_flow_end *nfe, struct flow_end_data *fe)
123 {
124     struct netflow_v5_header *nf_hdr;
125     struct netflow_v5_record *nf_rec;
126     uint8_t buf[sizeof(*nf_hdr) + sizeof(*nf_rec)];
127     uint8_t *p = buf;
128     struct timeval now;
129
130     /* We only send NetFlow messages for fully specified IP flows; any 
131      * entry with a wildcard is ignored. */
132     if ((nfe->match.wildcards != 0) 
133             || (nfe->match.dl_type != htons(ETH_TYPE_IP))) {
134         return;
135     }
136
137     memset(&buf, 0, sizeof(buf));
138     gettimeofday(&now, NULL);
139
140     nf_hdr = (struct netflow_v5_header *)p;
141     p += sizeof(*nf_hdr);
142     nf_rec = (struct netflow_v5_record *)p;
143
144     nf_hdr->version = htons(NETFLOW_V5_VERSION);
145     nf_hdr->count = htons(1);
146     nf_hdr->sysuptime = htonl((uint32_t)ntohll(nfe->end_time));
147     nf_hdr->unix_secs = htonl(now.tv_sec);
148     nf_hdr->unix_nsecs = htonl(now.tv_usec * 1000);
149     nf_hdr->flow_seq = htonl(fe->netflow_cnt);
150     nf_hdr->engine_type = 0;
151     nf_hdr->engine_id = 0;
152     nf_hdr->sampling_interval = htons(0);
153
154     nf_rec->src_addr = nfe->match.nw_src;
155     nf_rec->dst_addr = nfe->match.nw_dst;
156     nf_rec->nexthop = htons(0);
157     nf_rec->input = nfe->match.in_port;
158     nf_rec->output = htons(0);
159     nf_rec->packet_count = htonl((uint32_t)ntohll(nfe->packet_count));
160     nf_rec->byte_count = htonl((uint32_t)ntohll(nfe->byte_count));
161     nf_rec->init_time = htonl((uint32_t)ntohll(nfe->init_time));
162     nf_rec->used_time = htonl((uint32_t)ntohll(nfe->used_time));
163
164     if (nfe->match.nw_proto == IP_TYPE_ICMP) {
165         /* In NetFlow, the ICMP type and code are concatenated and
166          * placed in the 'dst_port' field. */
167         uint8_t type = (uint8_t)ntohs(nfe->match.tp_src);
168         uint8_t code = (uint8_t)ntohs(nfe->match.tp_dst);
169         nf_rec->src_port = htons(0);
170         nf_rec->dst_port = htons((type << 8) | code);
171     } else {
172         nf_rec->src_port = nfe->match.tp_src;
173         nf_rec->dst_port = nfe->match.tp_dst;
174     }
175
176     nf_rec->tcp_flags = nfe->tcp_flags;
177     nf_rec->ip_proto = nfe->match.nw_proto;
178     nf_rec->ip_tos = nfe->ip_tos;
179
180     nf_rec->src_as = htons(0);
181     nf_rec->dst_as = htons(0);
182     nf_rec->src_mask = 0;
183     nf_rec->dst_mask = 0;
184
185     send(fe->netflow_fd, buf, sizeof(buf), 0);
186     fe->netflow_cnt++;
187 }
188
189 static void 
190 send_ofp_expired(const struct nx_flow_end *nfe, const struct flow_end_data *fe)
191 {
192     struct ofp_flow_expired *ofe;
193     struct ofpbuf *b;
194
195     if ((nfe->reason != NXFER_IDLE_TIMEOUT) 
196             && (nfe->reason != NXFER_HARD_TIMEOUT)) {
197         return;
198     }
199
200     ofe = make_openflow(sizeof(*ofe), OFPT_FLOW_EXPIRED, &b);
201     ofe->match = nfe->match;
202     ofe->priority = nfe->priority;
203     if (nfe->reason == NXFER_IDLE_TIMEOUT) {
204         ofe->reason = OFPER_IDLE_TIMEOUT;
205     } else {
206         ofe->reason = OFPER_HARD_TIMEOUT;
207     }
208     /* 'duration' is in seconds, but we keeping track of milliseconds. */
209     ofe->duration = htonl((ntohll(nfe->end_time)-ntohll(nfe->init_time))/1000);
210     ofe->packet_count = nfe->packet_count;
211     ofe->byte_count = nfe->byte_count;
212
213     rconn_send(fe->remote_rconn, b, NULL);
214 }
215
216 static void 
217 send_nx_flow_end_config(const struct flow_end_data *fe)
218 {
219     struct nx_flow_end_config *nfec;
220     struct ofpbuf *b;
221
222     nfec = make_openflow(sizeof(*nfec), OFPT_VENDOR, &b);
223     nfec->header.vendor  = htonl(NX_VENDOR_ID);
224     nfec->header.subtype = htonl(NXT_FLOW_END_CONFIG);
225     if ((fe->send_ofp_exp == false) && (fe->netflow_fd < 0)) {
226         nfec->enable = 0;
227     } else {
228         nfec->enable = 1;
229     }
230
231     rconn_send(fe->local_rconn, b, NULL);
232 }
233
234 static bool
235 flow_end_local_packet_cb(struct relay *r, void *flow_end_)
236 {
237     struct flow_end_data *fe = flow_end_;
238     struct ofpbuf *msg = r->halves[HALF_LOCAL].rxbuf;
239     struct nicira_header *request = msg->data;
240     struct nx_flow_end *nfe = msg->data;
241
242
243     if (msg->size < sizeof(*nfe)) {
244         return false;
245     }
246     request = msg->data;
247     if (request->header.type != OFPT_VENDOR
248         || request->vendor != htonl(NX_VENDOR_ID)
249         || request->subtype != htonl(NXT_FLOW_END)) {
250         return false;
251     }
252
253     if (fe->netflow_fd >= 0) {
254         send_netflow_msg(nfe, fe);
255     }
256
257     if (fe->send_ofp_exp) {
258         send_ofp_expired(nfe, fe);
259     }
260
261     /* We always consume these Flow End messages. */
262     return true;
263 }
264
265 static bool
266 flow_end_remote_packet_cb(struct relay *r, void *flow_end_)
267 {
268     struct flow_end_data *fe = flow_end_;
269     struct ofpbuf *msg = r->halves[HALF_REMOTE].rxbuf;
270     struct ofp_switch_config *osc = msg->data;
271
272     /* Check for OFPT_SET_CONFIG messages to see if the controller wants
273      * to receive 'flow expired' messages.  If so, we need to intercept
274      * the datapath's 'flow end' meta-messages and convert. */
275
276     if ((msg->size < sizeof(*osc)) 
277             || (osc->header.type != OFPT_SET_CONFIG)) {
278         return false;
279     }
280
281     if (osc->flags & htons(OFPC_SEND_FLOW_EXP)) {
282         fe->send_ofp_exp = true;
283     } else {
284         fe->send_ofp_exp = false;
285     }
286
287     send_nx_flow_end_config(fe);
288
289     return false;
290 }
291
292 static struct hook_class flow_end_hook_class = {
293     flow_end_local_packet_cb,   /* local_packet_cb */
294     flow_end_remote_packet_cb,  /* remote_packet_cb */
295     NULL,                       /* periodic_cb */
296     NULL,                       /* wait_cb */
297     NULL,                       /* closing_cb */
298 };
299
300 void
301 flow_end_start(struct secchan *secchan, char *netflow_dst,
302                struct rconn *local, struct rconn *remote)
303 {
304     struct flow_end_data *fe;
305
306     fe = xcalloc(1, sizeof *fe);
307
308     fe->remote_rconn = remote;
309     fe->local_rconn = local;
310
311     if (netflow_dst) {
312         fe->netflow_fd = udp_open(netflow_dst);
313         if (fe->netflow_fd < 0) {
314             ofp_fatal(0, "NetFlow setup failed");
315         }
316         fe->send_ofp_exp = true;
317     } else {
318         fe->netflow_fd = -1;
319         fe->send_ofp_exp = false;
320     }
321
322     add_hook(secchan, &flow_end_hook_class, fe);
323
324     send_nx_flow_end_config(fe);
325 }