datapath-windows: Fix IP fragmentation
[cascardo/ovs.git] / lib / tnl-arp-cache.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 #include <inttypes.h>
19 #include <stdlib.h>
20
21 #include "bitmap.h"
22 #include "cmap.h"
23 #include "coverage.h"
24 #include "dpif-netdev.h"
25 #include "dynamic-string.h"
26 #include "errno.h"
27 #include "flow.h"
28 #include "netdev.h"
29 #include "ovs-thread.h"
30 #include "packets.h"
31 #include "poll-loop.h"
32 #include "seq.h"
33 #include "socket-util.h"
34 #include "timeval.h"
35 #include "tnl-arp-cache.h"
36 #include "unaligned.h"
37 #include "unixctl.h"
38 #include "util.h"
39 #include "openvswitch/vlog.h"
40
41
42 /* In seconds */
43 #define ARP_ENTRY_DEFAULT_IDLE_TIME  (15 * 60)
44
45 struct tnl_arp_entry {
46     struct cmap_node cmap_node;
47     ovs_be32 ip;
48     struct eth_addr mac;
49     time_t expires;             /* Expiration time. */
50     char br_name[IFNAMSIZ];
51 };
52
53 static struct cmap table;
54 static struct ovs_mutex mutex = OVS_MUTEX_INITIALIZER;
55
56 static struct tnl_arp_entry *
57 tnl_arp_lookup__(const char br_name[IFNAMSIZ], ovs_be32 dst)
58 {
59     struct tnl_arp_entry *arp;
60
61     CMAP_FOR_EACH_WITH_HASH (arp, cmap_node, (OVS_FORCE uint32_t) dst, &table) {
62         if (arp->ip == dst && !strcmp(arp->br_name, br_name)) {
63             arp->expires = time_now() + ARP_ENTRY_DEFAULT_IDLE_TIME;
64             return arp;
65         }
66     }
67     return NULL;
68 }
69
70 int
71 tnl_arp_lookup(const char br_name[IFNAMSIZ], ovs_be32 dst,
72                struct eth_addr *mac)
73 {
74     struct tnl_arp_entry *arp;
75     int res = ENOENT;
76
77     arp = tnl_arp_lookup__(br_name, dst);
78     if (arp) {
79         *mac = arp->mac;
80         res = 0;
81     }
82
83     return res;
84 }
85
86 static void
87 arp_entry_free(struct tnl_arp_entry *arp)
88 {
89     free(arp);
90 }
91
92 static void
93 tnl_arp_delete(struct tnl_arp_entry *arp)
94 {
95     cmap_remove(&table, &arp->cmap_node, (OVS_FORCE uint32_t) arp->ip);
96     ovsrcu_postpone(arp_entry_free, arp);
97 }
98
99 static void
100 tnl_arp_set(const char name[IFNAMSIZ], ovs_be32 dst, const struct eth_addr mac)
101 {
102     ovs_mutex_lock(&mutex);
103     struct tnl_arp_entry *arp = tnl_arp_lookup__(name, dst);
104     if (arp) {
105         if (eth_addr_equals(arp->mac, mac)) {
106             arp->expires = time_now() + ARP_ENTRY_DEFAULT_IDLE_TIME;
107             ovs_mutex_unlock(&mutex);
108             return;
109         }
110         tnl_arp_delete(arp);
111         seq_change(tnl_conf_seq);
112     }
113
114     arp = xmalloc(sizeof *arp);
115
116     arp->ip = dst;
117     arp->mac = mac;
118     arp->expires = time_now() + ARP_ENTRY_DEFAULT_IDLE_TIME;
119     ovs_strlcpy(arp->br_name, name, sizeof arp->br_name);
120     cmap_insert(&table, &arp->cmap_node, (OVS_FORCE uint32_t) arp->ip);
121     ovs_mutex_unlock(&mutex);
122 }
123
124 int
125 tnl_arp_snoop(const struct flow *flow, struct flow_wildcards *wc,
126               const char name[IFNAMSIZ])
127 {
128     if (flow->dl_type != htons(ETH_TYPE_ARP)) {
129         return EINVAL;
130     }
131
132     /* Exact Match on all ARP flows. */
133     memset(&wc->masks.nw_proto, 0xff, sizeof wc->masks.nw_proto);
134     memset(&wc->masks.nw_src, 0xff, sizeof wc->masks.nw_src);
135     memset(&wc->masks.arp_sha, 0xff, sizeof wc->masks.arp_sha);
136
137     tnl_arp_set(name, flow->nw_src, flow->arp_sha);
138
139     return 0;
140 }
141
142 void
143 tnl_arp_cache_run(void)
144 {
145     struct tnl_arp_entry *arp;
146     bool changed = false;
147
148     ovs_mutex_lock(&mutex);
149     CMAP_FOR_EACH(arp, cmap_node, &table) {
150         if (arp->expires <= time_now()) {
151             tnl_arp_delete(arp);
152             changed = true;
153         }
154     }
155     ovs_mutex_unlock(&mutex);
156
157     if (changed) {
158         seq_change(tnl_conf_seq);
159     }
160 }
161
162 static void
163 tnl_arp_cache_flush(struct unixctl_conn *conn, int argc OVS_UNUSED,
164                     const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED)
165 {
166     struct tnl_arp_entry *arp;
167     bool changed = false;
168
169     ovs_mutex_lock(&mutex);
170     CMAP_FOR_EACH(arp, cmap_node, &table) {
171         tnl_arp_delete(arp);
172         changed = true;
173     }
174     ovs_mutex_unlock(&mutex);
175     if (changed) {
176         seq_change(tnl_conf_seq);
177     }
178     unixctl_command_reply(conn, "OK");
179 }
180
181 static void
182 tnl_arp_cache_add(struct unixctl_conn *conn, int argc OVS_UNUSED,
183                   const char *argv[], void *aux OVS_UNUSED)
184 {
185     const char *br_name = argv[1];
186     struct eth_addr mac;
187     struct in_addr ip;
188
189     if (lookup_ip(argv[2], &ip)) {
190         unixctl_command_reply_error(conn, "bad IP address");
191         return;
192     }
193
194     if (!eth_addr_from_string(argv[3], &mac)) {
195         unixctl_command_reply_error(conn, "bad MAC address");
196         return;
197     }
198
199     tnl_arp_set(br_name, ip.s_addr, mac);
200     unixctl_command_reply(conn, "OK");
201 }
202
203 #define MAX_IP_ADDR_LEN 17
204
205 static void
206 tnl_arp_cache_show(struct unixctl_conn *conn, int argc OVS_UNUSED,
207                    const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED)
208 {
209     struct ds ds = DS_EMPTY_INITIALIZER;
210     struct tnl_arp_entry *arp;
211
212     ds_put_cstr(&ds, "IP               MAC                 Bridge\n");
213     ds_put_cstr(&ds, "=============================================\n");
214     ovs_mutex_lock(&mutex);
215     CMAP_FOR_EACH(arp, cmap_node, &table) {
216         int start_len, need_ws;
217
218         start_len = ds.length;
219         ds_put_format(&ds, IP_FMT, IP_ARGS(arp->ip));
220
221         need_ws = MAX_IP_ADDR_LEN - (ds.length - start_len);
222         ds_put_char_multiple(&ds, ' ', need_ws);
223
224         ds_put_format(&ds, ETH_ADDR_FMT"   %s\n",
225                       ETH_ADDR_ARGS(arp->mac), arp->br_name);
226
227     }
228     ovs_mutex_unlock(&mutex);
229     unixctl_command_reply(conn, ds_cstr(&ds));
230     ds_destroy(&ds);
231 }
232
233 void
234 tnl_arp_cache_init(void)
235 {
236     cmap_init(&table);
237
238     unixctl_command_register("tnl/arp/show", "", 0, 0, tnl_arp_cache_show, NULL);
239     unixctl_command_register("tnl/arp/set", "BRIDGE IP MAC", 3, 3, tnl_arp_cache_add, NULL);
240     unixctl_command_register("tnl/arp/flush", "", 0, 0, tnl_arp_cache_flush, NULL);
241 }