]>
Commit | Line | Data |
---|---|---|
a36de779 | 1 | /* |
8742957c | 2 | * Copyright (c) 2014, 2015 Nicira, Inc. |
a36de779 PS |
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" | |
a36de779 PS |
31 | #include "poll-loop.h" |
32 | #include "seq.h" | |
33 | #include "timeval.h" | |
34 | #include "tnl-arp-cache.h" | |
35 | #include "unaligned.h" | |
36 | #include "unixctl.h" | |
37 | #include "util.h" | |
e6211adc | 38 | #include "openvswitch/vlog.h" |
a36de779 PS |
39 | |
40 | ||
41 | /* In seconds */ | |
42 | #define ARP_ENTRY_DEFAULT_IDLE_TIME (15 * 60) | |
43 | ||
44 | struct tnl_arp_entry { | |
45 | struct cmap_node cmap_node; | |
46 | ovs_be32 ip; | |
47 | uint8_t mac[ETH_ADDR_LEN]; | |
48 | time_t expires; /* Expiration time. */ | |
49 | char br_name[IFNAMSIZ]; | |
50 | }; | |
51 | ||
52 | static struct cmap table; | |
53 | static struct ovs_mutex mutex = OVS_MUTEX_INITIALIZER; | |
54 | ||
55 | static struct tnl_arp_entry * | |
56 | tnl_arp_lookup__(const char br_name[IFNAMSIZ], ovs_be32 dst) | |
57 | { | |
58 | struct tnl_arp_entry *arp; | |
59 | ||
60 | CMAP_FOR_EACH_WITH_HASH (arp, cmap_node, (OVS_FORCE uint32_t) dst, &table) { | |
61 | if (arp->ip == dst && !strcmp(arp->br_name, br_name)) { | |
62 | arp->expires = time_now() + ARP_ENTRY_DEFAULT_IDLE_TIME; | |
63 | return arp; | |
64 | } | |
65 | } | |
66 | return NULL; | |
67 | } | |
68 | ||
69 | int | |
70 | tnl_arp_lookup(const char br_name[IFNAMSIZ], ovs_be32 dst, | |
71 | uint8_t mac[ETH_ADDR_LEN]) | |
72 | { | |
73 | struct tnl_arp_entry *arp; | |
74 | int res = ENOENT; | |
75 | ||
76 | arp = tnl_arp_lookup__(br_name, dst); | |
77 | if (arp) { | |
78 | memcpy(mac, arp->mac, ETH_ADDR_LEN); | |
79 | res = 0; | |
80 | } | |
81 | ||
82 | return res; | |
83 | } | |
84 | ||
85 | static void | |
86 | arp_entry_free(struct tnl_arp_entry *arp) | |
87 | { | |
88 | free(arp); | |
89 | } | |
90 | ||
91 | static void | |
92 | tnl_arp_delete(struct tnl_arp_entry *arp) | |
93 | { | |
94 | cmap_remove(&table, &arp->cmap_node, (OVS_FORCE uint32_t) arp->ip); | |
95 | ovsrcu_postpone(arp_entry_free, arp); | |
96 | } | |
97 | ||
98 | int | |
99 | tnl_arp_snoop(const struct flow *flow, struct flow_wildcards *wc, | |
100 | const char name[IFNAMSIZ]) | |
101 | { | |
102 | struct tnl_arp_entry *arp; | |
103 | ||
104 | if (flow->dl_type != htons(ETH_TYPE_ARP)) { | |
105 | return EINVAL; | |
106 | } | |
107 | ||
108 | /* Exact Match on all ARP flows. */ | |
109 | memset(&wc->masks.nw_proto, 0xff, sizeof wc->masks.nw_proto); | |
110 | memset(&wc->masks.nw_src, 0xff, sizeof wc->masks.nw_src); | |
111 | memset(&wc->masks.arp_sha, 0xff, sizeof wc->masks.arp_sha); | |
112 | ||
113 | ovs_mutex_lock(&mutex); | |
114 | arp = tnl_arp_lookup__(name, flow->nw_src); | |
115 | if (arp) { | |
116 | if (!memcmp(arp->mac, flow->arp_sha, ETH_ADDR_LEN)) { | |
117 | arp->expires = time_now() + ARP_ENTRY_DEFAULT_IDLE_TIME; | |
118 | ovs_mutex_unlock(&mutex); | |
119 | return 0; | |
120 | } | |
121 | tnl_arp_delete(arp); | |
122 | seq_change(tnl_conf_seq); | |
123 | } | |
124 | ||
125 | arp = xmalloc(sizeof *arp); | |
126 | ||
127 | arp->ip = flow->nw_src; | |
128 | memcpy(arp->mac, flow->arp_sha, ETH_ADDR_LEN); | |
129 | arp->expires = time_now() + ARP_ENTRY_DEFAULT_IDLE_TIME; | |
8742957c | 130 | ovs_strlcpy(arp->br_name, name, sizeof arp->br_name); |
a36de779 PS |
131 | cmap_insert(&table, &arp->cmap_node, (OVS_FORCE uint32_t) arp->ip); |
132 | ovs_mutex_unlock(&mutex); | |
133 | return 0; | |
134 | } | |
135 | ||
136 | void | |
137 | tnl_arp_cache_run(void) | |
138 | { | |
139 | struct tnl_arp_entry *arp; | |
140 | bool changed = false; | |
141 | ||
142 | ovs_mutex_lock(&mutex); | |
143 | CMAP_FOR_EACH(arp, cmap_node, &table) { | |
144 | if (arp->expires <= time_now()) { | |
145 | tnl_arp_delete(arp); | |
146 | changed = true; | |
147 | } | |
148 | } | |
149 | ovs_mutex_unlock(&mutex); | |
150 | ||
151 | if (changed) { | |
152 | seq_change(tnl_conf_seq); | |
153 | } | |
154 | } | |
155 | ||
156 | static void | |
157 | tnl_arp_cache_flush(struct unixctl_conn *conn OVS_UNUSED, int argc OVS_UNUSED, | |
158 | const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED) | |
159 | { | |
160 | struct tnl_arp_entry *arp; | |
161 | bool changed = false; | |
162 | ||
163 | ovs_mutex_lock(&mutex); | |
164 | CMAP_FOR_EACH(arp, cmap_node, &table) { | |
165 | tnl_arp_delete(arp); | |
166 | changed = true; | |
167 | } | |
168 | ovs_mutex_unlock(&mutex); | |
169 | if (changed) { | |
170 | seq_change(tnl_conf_seq); | |
171 | } | |
172 | unixctl_command_reply(conn, "OK"); | |
173 | } | |
174 | ||
175 | #define MAX_IP_ADDR_LEN 17 | |
176 | ||
177 | static void | |
178 | tnl_arp_cache_show(struct unixctl_conn *conn, int argc OVS_UNUSED, | |
179 | const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED) | |
180 | { | |
181 | struct ds ds = DS_EMPTY_INITIALIZER; | |
182 | struct tnl_arp_entry *arp; | |
183 | ||
184 | ds_put_cstr(&ds, "IP MAC Bridge\n"); | |
185 | ds_put_cstr(&ds, "=============================================\n"); | |
186 | ovs_mutex_lock(&mutex); | |
187 | CMAP_FOR_EACH(arp, cmap_node, &table) { | |
188 | int start_len, need_ws; | |
189 | ||
190 | start_len = ds.length; | |
191 | ds_put_format(&ds, IP_FMT, IP_ARGS(arp->ip)); | |
192 | ||
193 | need_ws = MAX_IP_ADDR_LEN - (ds.length - start_len); | |
194 | ds_put_char_multiple(&ds, ' ', need_ws); | |
195 | ||
196 | ds_put_format(&ds, ETH_ADDR_FMT" %s\n", | |
197 | ETH_ADDR_ARGS(arp->mac), arp->br_name); | |
198 | ||
199 | } | |
200 | ovs_mutex_unlock(&mutex); | |
201 | unixctl_command_reply(conn, ds_cstr(&ds)); | |
202 | ds_destroy(&ds); | |
203 | } | |
204 | ||
205 | void | |
206 | tnl_arp_cache_init(void) | |
207 | { | |
208 | cmap_init(&table); | |
209 | ||
210 | unixctl_command_register("tnl/arp/show", "", 0, 0, tnl_arp_cache_show, NULL); | |
211 | unixctl_command_register("tnl/arp/flush", "", 0, 0, tnl_arp_cache_flush, NULL); | |
212 | } |