1 /* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
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:
17 * The above copyright notice and this permission notice shall be
18 * included in all copies or substantial portions of the Software.
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
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.
37 #include <arpa/inet.h>
43 #include "openflow/nicira-ext.h"
44 #include "openflow/openflow.h"
49 #include "socket-util.h"
53 #define THIS_MODULE VLM_flow_end
56 struct flow_end_data {
57 struct rconn *remote_rconn;
58 struct rconn *local_rconn;
60 bool send_ofp_exp; /* Send OpenFlow 'flow expired' messages? */
62 int netflow_fd; /* Socket for NetFlow collector. */
63 uint32_t netflow_cnt; /* Flow sequence number for NetFlow. */
70 const char *host_name;
71 const char *port_string;
72 struct sockaddr_in sin;
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);
83 ofp_error(0, "%s: bad peer name format", dst);
87 ofp_error(0, "%s: bad port format", dst);
91 memset(&sin, 0, sizeof sin);
92 sin.sin_family = AF_INET;
93 if (lookup_ip(host_name, &sin.sin_addr)) {
96 sin.sin_port = htons(atoi(port_string));
98 fd = socket(AF_INET, SOCK_DGRAM, 0);
100 VLOG_ERR("%s: socket: %s", dst, strerror(errno));
104 retval = set_nonblocking(fd);
110 retval = connect(fd, (struct sockaddr *) &sin, sizeof sin);
113 VLOG_ERR("%s: connect: %s", dst, strerror(error));
122 send_netflow_msg(const struct nx_flow_end *nfe, struct flow_end_data *fe)
124 struct netflow_v5_header *nf_hdr;
125 struct netflow_v5_record *nf_rec;
126 uint8_t buf[sizeof(*nf_hdr) + sizeof(*nf_rec)];
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))) {
137 memset(&buf, 0, sizeof(buf));
138 gettimeofday(&now, NULL);
140 nf_hdr = (struct netflow_v5_header *)p;
141 p += sizeof(*nf_hdr);
142 nf_rec = (struct netflow_v5_record *)p;
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);
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));
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);
172 nf_rec->src_port = nfe->match.tp_src;
173 nf_rec->dst_port = nfe->match.tp_dst;
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;
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;
185 send(fe->netflow_fd, buf, sizeof(buf), 0);
190 send_ofp_expired(const struct nx_flow_end *nfe, const struct flow_end_data *fe)
192 struct ofp_flow_expired *ofe;
195 if ((nfe->reason != NXFER_IDLE_TIMEOUT)
196 && (nfe->reason != NXFER_HARD_TIMEOUT)) {
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;
206 ofe->reason = OFPER_HARD_TIMEOUT;
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;
213 rconn_send(fe->remote_rconn, b, NULL);
217 send_nx_flow_end_config(const struct flow_end_data *fe)
219 struct nx_flow_end_config *nfec;
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 nfec->enable = fe->send_ofp_exp ? 1 : 0;
227 rconn_send(fe->local_rconn, b, NULL);
231 flow_end_local_packet_cb(struct relay *r, void *flow_end_)
233 struct flow_end_data *fe = flow_end_;
234 struct ofpbuf *msg = r->halves[HALF_LOCAL].rxbuf;
235 struct nicira_header *request = msg->data;
236 struct nx_flow_end *nfe = msg->data;
239 if (msg->size < sizeof(*nfe)) {
243 if (request->header.type != OFPT_VENDOR
244 || request->vendor != htonl(NX_VENDOR_ID)
245 || request->subtype != htonl(NXT_FLOW_END)) {
249 if (fe->netflow_fd >= 0) {
250 send_netflow_msg(nfe, fe);
253 if (fe->send_ofp_exp) {
254 send_ofp_expired(nfe, fe);
257 /* We always consume these Flow End messages. */
262 flow_end_remote_packet_cb(struct relay *r, void *flow_end_)
264 struct flow_end_data *fe = flow_end_;
265 struct ofpbuf *msg = r->halves[HALF_REMOTE].rxbuf;
266 struct ofp_switch_config *osc = msg->data;
268 /* Check for OFPT_SET_CONFIG messages to see if the controller wants
269 * to receive 'flow expired' messages. If so, we need to intercept
270 * the datapath's 'flow end' meta-messages and convert. */
272 if ((msg->size < sizeof(*osc))
273 || (osc->header.type != OFPT_SET_CONFIG)) {
277 if (osc->flags & htons(OFPC_SEND_FLOW_EXP)) {
278 fe->send_ofp_exp = true;
280 fe->send_ofp_exp = false;
283 send_nx_flow_end_config(fe);
288 static struct hook_class flow_end_hook_class = {
289 flow_end_local_packet_cb, /* local_packet_cb */
290 flow_end_remote_packet_cb, /* remote_packet_cb */
291 NULL, /* periodic_cb */
293 NULL, /* closing_cb */
297 flow_end_start(struct secchan *secchan, char *netflow_dst,
298 struct rconn *local, struct rconn *remote)
300 struct flow_end_data *fe;
302 fe = xcalloc(1, sizeof *fe);
304 fe->remote_rconn = remote;
305 fe->local_rconn = local;
308 fe->netflow_fd = udp_open(netflow_dst);
309 if (fe->netflow_fd < 0) {
310 ofp_fatal(0, "NetFlow setup failed");
312 fe->send_ofp_exp = true;
315 fe->send_ofp_exp = false;
318 add_hook(secchan, &flow_end_hook_class, fe);
320 send_nx_flow_end_config(fe);