]>
Commit | Line | Data |
---|---|---|
d9b4ebc5 PS |
1 | /* |
2 | * Copyright (c) 2014 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 <arpa/inet.h> | |
19 | #include <errno.h> | |
20 | #include <inttypes.h> | |
21 | #include <sys/socket.h> | |
22 | #include <net/if.h> | |
23 | #include <netinet/in.h> | |
24 | #include <stdarg.h> | |
25 | #include <stdlib.h> | |
26 | #include <string.h> | |
27 | #include <unistd.h> | |
28 | ||
29 | #include "classifier.h" | |
30 | #include "command-line.h" | |
31 | #include "compiler.h" | |
32 | #include "dpif.h" | |
33 | #include "dynamic-string.h" | |
34 | #include "netdev.h" | |
35 | #include "packets.h" | |
a36de779 | 36 | #include "seq.h" |
d9b4ebc5 | 37 | #include "ovs-router.h" |
2d2b28d5 | 38 | #include "ovs-router-linux.h" |
fccd7c09 | 39 | #include "ovs-thread.h" |
d9b4ebc5 PS |
40 | #include "unixctl.h" |
41 | #include "util.h" | |
42 | ||
fccd7c09 | 43 | static struct ovs_mutex mutex = OVS_MUTEX_INITIALIZER; |
d9b4ebc5 PS |
44 | static struct classifier cls; |
45 | ||
46 | struct ovs_router_entry { | |
47 | struct cls_rule cr; | |
48 | char output_bridge[IFNAMSIZ]; | |
49 | ovs_be32 gw; | |
50 | ovs_be32 nw_addr; | |
51 | uint8_t plen; | |
52 | uint8_t priority; | |
53 | }; | |
54 | ||
55 | static struct ovs_router_entry * | |
56 | ovs_router_entry_cast(const struct cls_rule *cr) | |
57 | { | |
58 | if (offsetof(struct ovs_router_entry, cr) == 0) { | |
59 | return CONTAINER_OF(cr, struct ovs_router_entry, cr); | |
60 | } else { | |
61 | return cr ? CONTAINER_OF(cr, struct ovs_router_entry, cr) : NULL; | |
62 | } | |
63 | } | |
64 | ||
65 | bool | |
66 | ovs_router_lookup(ovs_be32 ip_dst, char output_bridge[], ovs_be32 *gw) | |
67 | { | |
68 | const struct cls_rule *cr; | |
69 | struct flow flow = {.nw_dst = ip_dst}; | |
70 | ||
71 | cr = classifier_lookup(&cls, &flow, NULL); | |
72 | if (cr) { | |
73 | struct ovs_router_entry *p = ovs_router_entry_cast(cr); | |
74 | ||
75 | strncpy(output_bridge, p->output_bridge, IFNAMSIZ); | |
76 | *gw = p->gw; | |
77 | return true; | |
78 | } | |
79 | return false; | |
80 | } | |
81 | ||
82 | static void | |
83 | rt_entry_free(struct ovs_router_entry *p) | |
84 | { | |
85 | cls_rule_destroy(&p->cr); | |
86 | free(p); | |
87 | } | |
88 | ||
89 | static void rt_init_match(struct match *match, ovs_be32 ip_dst, uint8_t plen) | |
90 | { | |
91 | ovs_be32 mask; | |
92 | ||
86f35fb5 | 93 | mask = be32_prefix_mask(plen); |
d9b4ebc5 PS |
94 | |
95 | ip_dst &= mask; /* Clear out insignificant bits. */ | |
96 | memset(match, 0, sizeof *match); | |
97 | match->flow.nw_dst = ip_dst; | |
98 | match->wc.masks.nw_dst = mask; | |
99 | } | |
100 | ||
101 | static void | |
102 | ovs_router_insert__(uint8_t priority, ovs_be32 ip_dst, uint8_t plen, | |
103 | const char output_bridge[], | |
104 | ovs_be32 gw) | |
105 | { | |
106 | const struct cls_rule *cr; | |
107 | struct ovs_router_entry *p; | |
108 | struct match match; | |
109 | ||
110 | rt_init_match(&match, ip_dst, plen); | |
111 | ||
112 | p = xzalloc(sizeof *p); | |
113 | strncpy(p->output_bridge, output_bridge, IFNAMSIZ); | |
114 | p->gw = gw; | |
115 | p->nw_addr = match.flow.nw_dst; | |
116 | p->plen = plen; | |
117 | p->priority = priority; | |
118 | cls_rule_init(&p->cr, &match, priority); /* Longest prefix matches first. */ | |
119 | ||
fccd7c09 | 120 | ovs_mutex_lock(&mutex); |
d9b4ebc5 | 121 | cr = classifier_replace(&cls, &p->cr); |
fccd7c09 JR |
122 | ovs_mutex_unlock(&mutex); |
123 | ||
d9b4ebc5 PS |
124 | if (cr) { |
125 | /* An old rule with the same match was displaced. */ | |
126 | ovsrcu_postpone(rt_entry_free, ovs_router_entry_cast(cr)); | |
127 | } | |
a36de779 | 128 | seq_change(tnl_conf_seq); |
d9b4ebc5 PS |
129 | } |
130 | ||
131 | void | |
132 | ovs_router_insert(ovs_be32 ip_dst, uint8_t plen, const char output_bridge[], | |
133 | ovs_be32 gw) | |
134 | { | |
135 | ovs_router_insert__(plen, ip_dst, plen, output_bridge, gw); | |
136 | } | |
137 | ||
138 | static bool | |
139 | rt_entry_delete(uint8_t priority, ovs_be32 ip_dst, uint8_t plen) | |
140 | { | |
dfea28b3 | 141 | const struct cls_rule *cr; |
d9b4ebc5 PS |
142 | struct cls_rule rule; |
143 | struct match match; | |
144 | ||
145 | rt_init_match(&match, ip_dst, plen); | |
146 | ||
147 | cls_rule_init(&rule, &match, priority); | |
148 | ||
149 | /* Find the exact rule. */ | |
150 | cr = classifier_find_rule_exactly(&cls, &rule); | |
151 | if (cr) { | |
152 | /* Remove it. */ | |
fccd7c09 | 153 | ovs_mutex_lock(&mutex); |
d9b4ebc5 | 154 | cr = classifier_remove(&cls, cr); |
fccd7c09 | 155 | ovs_mutex_unlock(&mutex); |
d9b4ebc5 | 156 | |
fccd7c09 | 157 | if (cr) { |
d9b4ebc5 PS |
158 | ovsrcu_postpone(rt_entry_free, ovs_router_entry_cast(cr)); |
159 | return true; | |
160 | } | |
161 | } | |
162 | return false; | |
163 | } | |
164 | ||
165 | static bool | |
166 | scan_ipv4_route(const char *s, ovs_be32 *addr, unsigned int *plen) | |
167 | { | |
168 | int len, max_plen, n; | |
169 | int slen = strlen(s); | |
170 | uint8_t *ip = (uint8_t *)addr; | |
171 | ||
495a6c34 | 172 | *addr = htonl(0); |
d9b4ebc5 PS |
173 | if (!ovs_scan(s, "%"SCNu8"%n", &ip[0], &n)) { |
174 | return false; | |
175 | } | |
176 | len = n; | |
177 | max_plen = 8; | |
178 | for (int i = 1; i < 4; i++) { | |
179 | if (ovs_scan(s + len, ".%"SCNu8"%n", &ip[i], &n)) { | |
180 | len += n; | |
181 | max_plen += 8; | |
182 | } else { | |
183 | break; | |
184 | } | |
185 | } | |
186 | if (len == slen && max_plen == 32) { | |
187 | *plen = 32; | |
188 | return true; | |
189 | } | |
190 | if (ovs_scan(s + len, "/%u%n", plen, &n) | |
191 | && len + n == slen && *plen <= max_plen) { | |
192 | return true; | |
193 | } | |
194 | return false; | |
195 | } | |
196 | ||
197 | static void | |
198 | ovs_router_add(struct unixctl_conn *conn, int argc, | |
199 | const char *argv[], void *aux OVS_UNUSED) | |
200 | { | |
201 | ovs_be32 ip, gw; | |
202 | unsigned int plen; | |
203 | ||
204 | if (scan_ipv4_route(argv[1], &ip, &plen)) { | |
205 | if (argc > 3) { | |
206 | inet_pton(AF_INET, argv[3], (struct in_addr *)&gw); | |
207 | } else { | |
208 | gw = 0; | |
209 | } | |
210 | ovs_router_insert__(plen + 32, ip, plen, argv[2], gw); | |
211 | unixctl_command_reply(conn, "OK"); | |
212 | } else { | |
213 | unixctl_command_reply(conn, "Invalid parameters"); | |
214 | } | |
215 | } | |
216 | ||
217 | static void | |
218 | ovs_router_del(struct unixctl_conn *conn, int argc OVS_UNUSED, | |
219 | const char *argv[], void *aux OVS_UNUSED) | |
220 | { | |
221 | ovs_be32 ip; | |
222 | unsigned int plen; | |
223 | ||
224 | if (scan_ipv4_route(argv[1], &ip, &plen)) { | |
225 | ||
226 | if (rt_entry_delete(plen + 32, ip, plen)) { | |
227 | unixctl_command_reply(conn, "OK"); | |
a36de779 | 228 | seq_change(tnl_conf_seq); |
d9b4ebc5 PS |
229 | } else { |
230 | unixctl_command_reply(conn, "Not found"); | |
231 | } | |
232 | } else { | |
233 | unixctl_command_reply(conn, "Invalid parameters"); | |
234 | } | |
235 | } | |
236 | ||
237 | static void | |
238 | ovs_router_show(struct unixctl_conn *conn, int argc OVS_UNUSED, | |
239 | const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED) | |
240 | { | |
241 | struct ovs_router_entry *rt; | |
242 | struct ds ds = DS_EMPTY_INITIALIZER; | |
243 | ||
244 | ds_put_format(&ds, "Route Table:\n"); | |
245 | CLS_FOR_EACH(rt, cr, &cls) { | |
246 | if (rt->priority == rt->plen) { | |
247 | ds_put_format(&ds, "Cached: "); | |
248 | } else { | |
249 | ds_put_format(&ds, "User: "); | |
250 | } | |
251 | ds_put_format(&ds, IP_FMT"/%"PRIu16" dev %s", | |
252 | IP_ARGS(rt->nw_addr), rt->plen, | |
253 | rt->output_bridge); | |
254 | if (rt->gw) { | |
255 | ds_put_format(&ds, " GW "IP_FMT, IP_ARGS(rt->gw)); | |
256 | } | |
257 | ds_put_format(&ds, "\n"); | |
258 | } | |
259 | unixctl_command_reply(conn, ds_cstr(&ds)); | |
260 | ds_destroy(&ds); | |
261 | } | |
262 | ||
263 | void | |
264 | ovs_router_flush(void) | |
265 | { | |
266 | struct ovs_router_entry *rt; | |
267 | ||
802f84ff JR |
268 | ovs_mutex_lock(&mutex); |
269 | classifier_defer(&cls); | |
de4ad4a2 | 270 | CLS_FOR_EACH(rt, cr, &cls) { |
d9b4ebc5 | 271 | if (rt->priority == rt->plen) { |
fccd7c09 JR |
272 | if (classifier_remove(&cls, &rt->cr)) { |
273 | ovsrcu_postpone(rt_entry_free, rt); | |
274 | } | |
d9b4ebc5 PS |
275 | } |
276 | } | |
802f84ff JR |
277 | classifier_publish(&cls); |
278 | ovs_mutex_unlock(&mutex); | |
a36de779 | 279 | seq_change(tnl_conf_seq); |
d9b4ebc5 PS |
280 | } |
281 | ||
282 | /* May not be called more than once. */ | |
283 | void | |
1bc50ef3 | 284 | ovs_router_init(void) |
d9b4ebc5 PS |
285 | { |
286 | classifier_init(&cls, NULL); | |
a36de779 | 287 | unixctl_command_register("ovs/route/add", "ipv4_addr/prefix_len out_br_name gw", 2, 3, |
d9b4ebc5 PS |
288 | ovs_router_add, NULL); |
289 | unixctl_command_register("ovs/route/show", "", 0, 0, ovs_router_show, NULL); | |
a36de779 | 290 | unixctl_command_register("ovs/route/del", "ipv4_addr/prefix_len", 1, 1, ovs_router_del, |
d9b4ebc5 PS |
291 | NULL); |
292 | } |