tnl-ports: Include tnl-ports.h as first header.
[cascardo/ovs.git] / lib / tnl-ports.c
1 /*
2  * Copyright (c) 2014, 2015 Nicira, Inc.
3  *
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:
7  *
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.
15  */
16
17 #include <config.h>
18
19 #include "tnl-ports.h"
20
21 #include <stddef.h>
22 #include <stdint.h>
23
24 #include "classifier.h"
25 #include "dynamic-string.h"
26 #include "hash.h"
27 #include "list.h"
28 #include "ofpbuf.h"
29 #include "ovs-thread.h"
30 #include "odp-util.h"
31 #include "tnl-arp-cache.h"
32 #include "ovs-thread.h"
33 #include "unixctl.h"
34 #include "util.h"
35
36 static struct ovs_mutex mutex = OVS_MUTEX_INITIALIZER;
37 static struct classifier cls;   /* Tunnel ports. */
38
39 struct ip_device {
40     struct netdev *dev;
41     struct eth_addr mac;
42     ovs_be32 addr;
43     uint64_t change_seq;
44     struct ovs_list node;
45     char dev_name[IFNAMSIZ];
46 };
47
48 static struct ovs_list addr_list;
49
50 struct tnl_port {
51     odp_port_t port;
52     ovs_be16 udp_port;
53     char dev_name[IFNAMSIZ];
54     struct ovs_list node;
55 };
56
57 static struct ovs_list port_list;
58
59 struct tnl_port_in {
60     struct cls_rule cr;
61     odp_port_t portno;
62     struct ovs_refcount ref_cnt;
63     char dev_name[IFNAMSIZ];
64 };
65
66 static struct tnl_port_in *
67 tnl_port_cast(const struct cls_rule *cr)
68 {
69     BUILD_ASSERT_DECL(offsetof(struct tnl_port_in, cr) == 0);
70
71     return CONTAINER_OF(cr, struct tnl_port_in, cr);
72 }
73
74 static void
75 tnl_port_free(struct tnl_port_in *p)
76 {
77     cls_rule_destroy(&p->cr);
78     free(p);
79 }
80
81 static void
82 tnl_port_init_flow(struct flow *flow, struct eth_addr mac,
83                    ovs_be32 addr, ovs_be16 udp_port)
84 {
85     memset(flow, 0, sizeof *flow);
86
87     flow->dl_type = htons(ETH_TYPE_IP);
88     flow->dl_dst = mac;
89     flow->nw_dst = addr;
90
91     if (udp_port) {
92         flow->nw_proto = IPPROTO_UDP;
93     } else {
94         flow->nw_proto = IPPROTO_GRE;
95     }
96     flow->tp_dst = udp_port;
97 }
98
99 static void
100 map_insert(odp_port_t port, struct eth_addr mac, ovs_be32 addr,
101            ovs_be16 udp_port, const char dev_name[])
102 {
103     const struct cls_rule *cr;
104     struct tnl_port_in *p;
105     struct match match;
106
107     memset(&match, 0, sizeof match);
108     tnl_port_init_flow(&match.flow, mac, addr, udp_port);
109
110     do {
111         cr = classifier_lookup(&cls, CLS_MAX_VERSION, &match.flow, NULL);
112         p = tnl_port_cast(cr);
113         /* Try again if the rule was released before we get the reference. */
114     } while (p && !ovs_refcount_try_ref_rcu(&p->ref_cnt));
115
116     if (!p) {
117         p = xzalloc(sizeof *p);
118         p->portno = port;
119
120         match.wc.masks.dl_type = OVS_BE16_MAX;
121         match.wc.masks.nw_proto = 0xff;
122         match.wc.masks.nw_frag = 0xff;      /* XXX: No fragments support. */
123         match.wc.masks.tp_dst = OVS_BE16_MAX;
124         match.wc.masks.nw_dst = OVS_BE32_MAX;
125         match.wc.masks.vlan_tci = OVS_BE16_MAX;
126         memset(&match.wc.masks.dl_dst, 0xff, sizeof (struct eth_addr));
127
128         cls_rule_init(&p->cr, &match, 0); /* Priority == 0. */
129         ovs_refcount_init(&p->ref_cnt);
130         ovs_strlcpy(p->dev_name, dev_name, sizeof p->dev_name);
131
132         classifier_insert(&cls, &p->cr, CLS_MIN_VERSION, NULL, 0);
133     }
134 }
135
136 void
137 tnl_port_map_insert(odp_port_t port,
138                     ovs_be16 udp_port, const char dev_name[])
139 {
140     struct tnl_port *p;
141     struct ip_device *ip_dev;
142
143     ovs_mutex_lock(&mutex);
144     LIST_FOR_EACH(p, node, &port_list) {
145         if (udp_port == p->udp_port) {
146              goto out;
147         }
148     }
149
150     p = xzalloc(sizeof *p);
151     p->port = port;
152     p->udp_port = udp_port;
153     ovs_strlcpy(p->dev_name, dev_name, sizeof p->dev_name);
154     list_insert(&port_list, &p->node);
155
156     LIST_FOR_EACH(ip_dev, node, &addr_list) {
157         map_insert(p->port, ip_dev->mac, ip_dev->addr,
158                    p->udp_port, p->dev_name);
159     }
160
161 out:
162     ovs_mutex_unlock(&mutex);
163 }
164
165 static void
166 tnl_port_unref(const struct cls_rule *cr)
167 {
168     struct tnl_port_in *p = tnl_port_cast(cr);
169
170     if (cr && ovs_refcount_unref_relaxed(&p->ref_cnt) == 1) {
171         if (classifier_remove(&cls, cr)) {
172             ovsrcu_postpone(tnl_port_free, p);
173         }
174     }
175 }
176
177 static void
178 map_delete(struct eth_addr mac, ovs_be32 addr, ovs_be16 udp_port)
179 {
180     const struct cls_rule *cr;
181     struct flow flow;
182
183     tnl_port_init_flow(&flow, mac, addr, udp_port);
184
185     cr = classifier_lookup(&cls, CLS_MAX_VERSION, &flow, NULL);
186     tnl_port_unref(cr);
187 }
188
189 void
190 tnl_port_map_delete(ovs_be16 udp_port)
191 {
192     struct tnl_port *p, *next;
193     struct ip_device *ip_dev;
194     bool found = false;
195
196     ovs_mutex_lock(&mutex);
197     LIST_FOR_EACH_SAFE(p, next, node, &port_list) {
198         if (p->udp_port == udp_port) {
199             list_remove(&p->node);
200             found = true;
201             break;
202         }
203     }
204
205     if (!found) {
206         goto out;
207     }
208     LIST_FOR_EACH(ip_dev, node, &addr_list) {
209         map_delete(ip_dev->mac, ip_dev->addr, udp_port);
210     }
211
212     free(p);
213 out:
214     ovs_mutex_unlock(&mutex);
215 }
216
217 /* 'flow' is non-const to allow for temporary modifications during the lookup.
218  * Any changes are restored before returning. */
219 odp_port_t
220 tnl_port_map_lookup(struct flow *flow, struct flow_wildcards *wc)
221 {
222     const struct cls_rule *cr = classifier_lookup(&cls, CLS_MAX_VERSION, flow,
223                                                   wc);
224
225     return (cr) ? tnl_port_cast(cr)->portno : ODPP_NONE;
226 }
227
228 static void
229 tnl_port_show_v(struct ds *ds)
230 {
231     const struct tnl_port_in *p;
232
233     CLS_FOR_EACH(p, cr, &cls) {
234         struct odputil_keybuf keybuf;
235         struct odputil_keybuf maskbuf;
236         struct flow flow;
237         const struct nlattr *key, *mask;
238         size_t key_len, mask_len;
239         struct flow_wildcards wc;
240         struct ofpbuf buf;
241         struct odp_flow_key_parms odp_parms = {
242             .flow = &flow,
243             .mask = &wc.masks,
244         };
245
246         ds_put_format(ds, "%s (%"PRIu32") : ", p->dev_name, p->portno);
247         minimask_expand(p->cr.match.mask, &wc);
248         miniflow_expand(p->cr.match.flow, &flow);
249
250         /* Key. */
251         odp_parms.odp_in_port = flow.in_port.odp_port;
252         odp_parms.support.recirc = true;
253         ofpbuf_use_stack(&buf, &keybuf, sizeof keybuf);
254         odp_flow_key_from_flow(&odp_parms, &buf);
255         key = buf.data;
256         key_len = buf.size;
257
258         /* mask*/
259         odp_parms.odp_in_port = wc.masks.in_port.odp_port;
260         odp_parms.support.recirc = false;
261         ofpbuf_use_stack(&buf, &maskbuf, sizeof maskbuf);
262         odp_flow_key_from_mask(&odp_parms, &buf);
263         mask = buf.data;
264         mask_len = buf.size;
265
266         /* build string. */
267         odp_flow_format(key, key_len, mask, mask_len, NULL, ds, false);
268         ds_put_format(ds, "\n");
269     }
270 }
271
272 static void
273 tnl_port_show(struct unixctl_conn *conn, int argc OVS_UNUSED,
274                const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED)
275 {
276     struct ds ds = DS_EMPTY_INITIALIZER;
277     struct tnl_port *p;
278
279     ds_put_format(&ds, "Listening ports:\n");
280     ovs_mutex_lock(&mutex);
281     if (argc > 1) {
282         if (!strcasecmp(argv[1], "-v")) {
283             tnl_port_show_v(&ds);
284             goto out;
285         }
286     }
287
288     LIST_FOR_EACH(p, node, &port_list) {
289         ds_put_format(&ds, "%s (%"PRIu32")\n", p->dev_name, p->port);
290     }
291
292 out:
293     ovs_mutex_unlock(&mutex);
294     unixctl_command_reply(conn, ds_cstr(&ds));
295     ds_destroy(&ds);
296 }
297
298 static void
299 map_insert_ipdev(struct ip_device *ip_dev)
300 {
301     struct tnl_port *p;
302
303     LIST_FOR_EACH(p, node, &port_list) {
304         map_insert(p->port, ip_dev->mac, ip_dev->addr,
305                    p->udp_port, p->dev_name);
306     }
307 }
308
309 static void
310 insert_ipdev(const char dev_name[])
311 {
312     struct ip_device *ip_dev;
313     enum netdev_flags flags;
314     struct netdev *dev;
315     int error;
316
317     error = netdev_open(dev_name, NULL, &dev);
318     if (error) {
319         return;
320     }
321
322     error = netdev_get_flags(dev, &flags);
323     if (error || (flags & NETDEV_LOOPBACK)) {
324         netdev_close(dev);
325         return;
326     }
327
328     ip_dev = xzalloc(sizeof *ip_dev);
329     ip_dev->dev = dev;
330     ip_dev->change_seq = netdev_get_change_seq(dev);
331     error = netdev_get_etheraddr(ip_dev->dev, &ip_dev->mac);
332     if (error) {
333         return;
334     }
335     error = netdev_get_in4(ip_dev->dev, (struct in_addr *)&ip_dev->addr, NULL);
336     if (error) {
337         return;
338     }
339     ovs_strlcpy(ip_dev->dev_name, netdev_get_name(dev), sizeof ip_dev->dev_name);
340
341     list_insert(&addr_list, &ip_dev->node);
342     map_insert_ipdev(ip_dev);
343 }
344
345 static void
346 delete_ipdev(struct ip_device *ip_dev)
347 {
348     struct tnl_port *p;
349
350     LIST_FOR_EACH(p, node, &port_list) {
351         map_delete(ip_dev->mac, ip_dev->addr, p->udp_port);
352     }
353
354     list_remove(&ip_dev->node);
355     netdev_close(ip_dev->dev);
356     free(ip_dev);
357 }
358
359 void
360 tnl_port_map_insert_ipdev(const char dev_name[])
361 {
362     struct ip_device *ip_dev, *next;
363
364     ovs_mutex_lock(&mutex);
365
366     LIST_FOR_EACH_SAFE(ip_dev, next, node, &addr_list) {
367         if (!strcmp(netdev_get_name(ip_dev->dev), dev_name)) {
368             if (ip_dev->change_seq == netdev_get_change_seq(ip_dev->dev)) {
369                 goto out;
370             }
371             /* Address changed. */
372             delete_ipdev(ip_dev);
373             break;
374         }
375     }
376     insert_ipdev(dev_name);
377
378 out:
379     ovs_mutex_unlock(&mutex);
380 }
381
382 void
383 tnl_port_map_delete_ipdev(const char dev_name[])
384 {
385     struct ip_device *ip_dev, *next;
386
387     ovs_mutex_lock(&mutex);
388     LIST_FOR_EACH_SAFE(ip_dev, next, node, &addr_list) {
389         if (!strcmp(netdev_get_name(ip_dev->dev), dev_name)) {
390             delete_ipdev(ip_dev);
391         }
392     }
393     ovs_mutex_unlock(&mutex);
394 }
395
396 void
397 tnl_port_map_run(void)
398 {
399     struct ip_device *ip_dev, *next;
400
401     ovs_mutex_lock(&mutex);
402     LIST_FOR_EACH_SAFE(ip_dev, next, node, &addr_list) {
403         char dev_name[IFNAMSIZ];
404
405         if (ip_dev->change_seq == netdev_get_change_seq(ip_dev->dev)) {
406             continue;
407         }
408
409         /* Address changed. */
410         ovs_strlcpy(dev_name, ip_dev->dev_name, sizeof dev_name);
411         delete_ipdev(ip_dev);
412         insert_ipdev(dev_name);
413     }
414     ovs_mutex_unlock(&mutex);
415 }
416
417 void
418 tnl_port_map_init(void)
419 {
420     classifier_init(&cls, flow_segment_u64s);
421     list_init(&addr_list);
422     list_init(&port_list);
423     unixctl_command_register("tnl/ports/show", "-v", 0, 1, tnl_port_show, NULL);
424 }