Line data Source code
1 : /* Hey Emacs use -*- mode: C -*- */
2 : /*
3 : * Copyright 2021 Cisco and/or its affiliates.
4 : *
5 : * Licensed under the Apache License, Version 2.0 (the "License");
6 : * you may not use this file except in compliance with the License.
7 : * You may obtain a copy of the License at:
8 : *
9 : * http://www.apache.org/licenses/LICENSE-2.0
10 : *
11 : * Unless required by applicable law or agreed to in writing, software
12 : * distributed under the License is distributed on an "AS IS" BASIS,
13 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 : * See the License for the specific language governing permissions and
15 : * limitations under the License.
16 : */
17 :
18 : #include <vnet/vnet.h>
19 : #include <vnet/plugin/plugin.h>
20 : #include <vnet/devices/netlink.h>
21 : #include <vnet/ip/ip.h>
22 : #include <vppinfra/linux/netns.h>
23 : #include <plugins/linux-cp/lcp_interface.h>
24 :
25 : /* helper function to copy forward all sw interface link state flags
26 : * MTU, and IP addresses into their counterpart LIP interface.
27 : *
28 : * This is called upon MTU changes and state changes.
29 : */
30 : void
31 0 : lcp_itf_pair_sync_state (lcp_itf_pair_t *lip)
32 : {
33 : vnet_sw_interface_t *sw;
34 : vnet_sw_interface_t *sup_sw;
35 0 : int curr_ns_fd = -1;
36 0 : int vif_ns_fd = -1;
37 : u32 mtu;
38 : u32 netlink_mtu;
39 :
40 0 : if (!lcp_sync ())
41 0 : return;
42 :
43 : sw =
44 0 : vnet_get_sw_interface_or_null (vnet_get_main (), lip->lip_phy_sw_if_index);
45 0 : if (!sw)
46 0 : return;
47 : sup_sw =
48 0 : vnet_get_sw_interface_or_null (vnet_get_main (), sw->sup_sw_if_index);
49 0 : if (!sup_sw)
50 0 : return;
51 :
52 0 : if (lip->lip_namespace)
53 : {
54 0 : curr_ns_fd = clib_netns_open (NULL /* self */);
55 0 : vif_ns_fd = clib_netns_open (lip->lip_namespace);
56 0 : if (vif_ns_fd != -1)
57 0 : clib_setns (vif_ns_fd);
58 : }
59 :
60 0 : LCP_ITF_PAIR_INFO ("sync_state: %U flags %u sup-flags %u mtu %u sup-mtu %u",
61 : format_lcp_itf_pair, lip, sw->flags, sup_sw->flags,
62 : sw->mtu[VNET_MTU_L3], sup_sw->mtu[VNET_MTU_L3]);
63 :
64 : /* Linux will not allow children to be admin-up if their parent is
65 : * admin-down. If child is up but parent is not, force it down.
66 : */
67 0 : int state = sw->flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP;
68 :
69 0 : if (state && !(sup_sw->flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP))
70 : {
71 0 : LCP_ITF_PAIR_WARN (
72 : "sync_state: %U flags %u sup-flags %u mtu %u sup-mtu %u: "
73 : "forcing state to sup-flags to satisfy netlink",
74 : format_lcp_itf_pair, lip, sw->flags, sup_sw->flags,
75 : sw->mtu[VNET_MTU_L3], sup_sw->mtu[VNET_MTU_L3]);
76 0 : state = 0;
77 : }
78 0 : lcp_itf_set_link_state (lip, state);
79 :
80 : /* Linux will clamp MTU of children when the parent is lower. VPP is fine
81 : * with differing MTUs. VPP assumes that if a subint has MTU of 0, that it
82 : * inherits from its parent. Linux likes to be more explicit, so we
83 : * reconcile any differences.
84 : */
85 0 : mtu = sw->mtu[VNET_MTU_L3];
86 0 : if (mtu == 0)
87 0 : mtu = sup_sw->mtu[VNET_MTU_L3];
88 :
89 0 : if (sup_sw->mtu[VNET_MTU_L3] < sw->mtu[VNET_MTU_L3])
90 : {
91 0 : LCP_ITF_PAIR_WARN ("sync_state: %U flags %u mtu %u sup-mtu %u: "
92 : "clamping to sup-mtu to satisfy netlink",
93 : format_lcp_itf_pair, lip, sw->flags,
94 : sw->mtu[VNET_MTU_L3], sup_sw->mtu[VNET_MTU_L3]);
95 0 : mtu = sup_sw->mtu[VNET_MTU_L3];
96 : }
97 :
98 : /* Set MTU on all of {sw, tap, netlink}. Only send a netlink message if we
99 : * really do want to change the MTU.
100 : */
101 0 : vnet_sw_interface_set_mtu (vnet_get_main (), lip->lip_phy_sw_if_index, mtu);
102 0 : vnet_sw_interface_set_mtu (vnet_get_main (), lip->lip_host_sw_if_index, mtu);
103 0 : if (NULL == vnet_netlink_get_link_mtu (lip->lip_vif_index, &netlink_mtu))
104 : {
105 0 : if (netlink_mtu != mtu)
106 0 : vnet_netlink_set_link_mtu (lip->lip_vif_index, mtu);
107 : }
108 :
109 : /* Linux will remove IPv6 addresses on children when the parent state
110 : * goes down, so we ensure all IPv4/IPv6 addresses are synced.
111 : */
112 0 : lcp_itf_set_interface_addr (lip);
113 :
114 0 : if (vif_ns_fd != -1)
115 0 : close (vif_ns_fd);
116 :
117 0 : if (curr_ns_fd != -1)
118 : {
119 0 : clib_setns (curr_ns_fd);
120 0 : close (curr_ns_fd);
121 : }
122 :
123 0 : return;
124 : }
125 :
126 : static walk_rc_t
127 0 : lcp_itf_pair_walk_sync_state_all_cb (index_t lipi, void *ctx)
128 : {
129 : lcp_itf_pair_t *lip;
130 0 : lip = lcp_itf_pair_get (lipi);
131 0 : if (!lip)
132 0 : return WALK_CONTINUE;
133 :
134 0 : lcp_itf_pair_sync_state (lip);
135 0 : return WALK_CONTINUE;
136 : }
137 :
138 : static walk_rc_t
139 0 : lcp_itf_pair_walk_sync_state_hw_cb (vnet_main_t *vnm, u32 sw_if_index,
140 : void *arg)
141 : {
142 : lcp_itf_pair_t *lip;
143 :
144 0 : lip = lcp_itf_pair_get (lcp_itf_pair_find_by_phy (sw_if_index));
145 0 : if (!lip)
146 : {
147 0 : return WALK_CONTINUE;
148 : }
149 :
150 0 : lcp_itf_pair_sync_state (lip);
151 0 : return WALK_CONTINUE;
152 : }
153 :
154 : void
155 0 : lcp_itf_pair_sync_state_all ()
156 : {
157 0 : lcp_itf_pair_walk (lcp_itf_pair_walk_sync_state_all_cb, 0);
158 0 : }
159 :
160 : void
161 0 : lcp_itf_pair_sync_state_hw (vnet_hw_interface_t *hi)
162 : {
163 0 : if (!hi)
164 0 : return;
165 0 : LCP_ITF_PAIR_DBG ("sync_state_hw: hi %U", format_vnet_sw_if_index_name,
166 : vnet_get_main (), hi->hw_if_index);
167 :
168 0 : vnet_hw_interface_walk_sw (vnet_get_main (), hi->hw_if_index,
169 : lcp_itf_pair_walk_sync_state_hw_cb, NULL);
170 : }
171 :
172 : static clib_error_t *
173 37 : lcp_itf_admin_state_change (vnet_main_t *vnm, u32 sw_if_index, u32 flags)
174 : {
175 : lcp_itf_pair_t *lip;
176 : vnet_hw_interface_t *hi;
177 : vnet_sw_interface_t *si;
178 :
179 37 : if (!lcp_sync ())
180 37 : return 0;
181 :
182 0 : LCP_ITF_PAIR_DBG ("admin_state_change: sw %U %u",
183 : format_vnet_sw_if_index_name, vnm, sw_if_index, flags);
184 :
185 : // Sync interface state changes into host
186 0 : lip = lcp_itf_pair_get (lcp_itf_pair_find_by_phy (sw_if_index));
187 0 : if (!lip)
188 0 : return NULL;
189 0 : LCP_ITF_PAIR_INFO ("admin_state_change: %U flags %u", format_lcp_itf_pair,
190 : lip, flags);
191 :
192 0 : if (vnet_sw_interface_is_sub (vnm, sw_if_index))
193 : {
194 0 : lcp_itf_pair_sync_state (lip);
195 0 : return NULL;
196 : }
197 :
198 : // When Linux changes link on a parent interface, all of its children also
199 : // change. If a parent interface changes MTU, all of its children are clamped
200 : // at that MTU by Linux. Neither holds true in VPP, so we are forced to undo
201 : // change by walking the sub-interfaces of a phy and syncing their state back
202 : // into Linux.
203 0 : si = vnet_get_sw_interface_or_null (vnm, sw_if_index);
204 0 : if (!si)
205 0 : return NULL;
206 :
207 0 : hi = vnet_get_hw_interface_or_null (vnm, si->hw_if_index);
208 0 : if (!hi)
209 0 : return NULL;
210 0 : LCP_ITF_PAIR_DBG ("admin_state_change: si %U hi %U, syncing children",
211 : format_vnet_sw_if_index_name, vnm, si->sw_if_index,
212 : format_vnet_sw_if_index_name, vnm, hi->sw_if_index);
213 :
214 0 : lcp_itf_pair_sync_state_hw (hi);
215 :
216 0 : return NULL;
217 : }
218 :
219 6 : VNET_SW_INTERFACE_ADMIN_UP_DOWN_FUNCTION (lcp_itf_admin_state_change);
220 :
221 : static clib_error_t *
222 10 : lcp_itf_mtu_change (vnet_main_t *vnm, u32 sw_if_index, u32 flags)
223 : {
224 : vnet_sw_interface_t *si;
225 : vnet_hw_interface_t *hi;
226 10 : if (!lcp_sync ())
227 10 : return NULL;
228 :
229 0 : LCP_ITF_PAIR_DBG ("mtu_change: sw %U %u", format_vnet_sw_if_index_name, vnm,
230 : sw_if_index, flags);
231 :
232 0 : if (vnet_sw_interface_is_sub (vnm, sw_if_index))
233 : {
234 : lcp_itf_pair_t *lip;
235 0 : lip = lcp_itf_pair_get (lcp_itf_pair_find_by_phy (sw_if_index));
236 0 : if (lip)
237 0 : lcp_itf_pair_sync_state (lip);
238 0 : return NULL;
239 : }
240 :
241 : // When Linux changes link on a parent interface, all of its children also
242 : // change. If a parent interface changes MTU, all of its children are clamped
243 : // at that MTU by Linux. Neither holds true in VPP, so we are forced to undo
244 : // change by walking the sub-interfaces of a phy and syncing their state back
245 : // into Linux.
246 0 : si = vnet_get_sw_interface_or_null (vnm, sw_if_index);
247 0 : if (!si)
248 0 : return NULL;
249 :
250 0 : hi = vnet_get_hw_interface_or_null (vnm, si->hw_if_index);
251 0 : if (!hi)
252 0 : return NULL;
253 0 : LCP_ITF_PAIR_DBG ("mtu_change: si %U hi %U, syncing children",
254 : format_vnet_sw_if_index_name, vnm, si->sw_if_index,
255 : format_vnet_sw_if_index_name, vnm, hi->sw_if_index);
256 :
257 0 : lcp_itf_pair_sync_state_hw (hi);
258 :
259 0 : return NULL;
260 : }
261 :
262 4 : VNET_SW_INTERFACE_MTU_CHANGE_FUNCTION (lcp_itf_mtu_change);
263 :
264 : static void
265 16 : lcp_itf_ip4_add_del_interface_addr (ip4_main_t *im, uword opaque,
266 : u32 sw_if_index, ip4_address_t *address,
267 : u32 address_length, u32 if_address_index,
268 : u32 is_del)
269 : {
270 : const lcp_itf_pair_t *lip;
271 16 : int curr_ns_fd = -1;
272 16 : int vif_ns_fd = -1;
273 :
274 16 : if (!lcp_sync ())
275 16 : return;
276 :
277 0 : LCP_ITF_PAIR_DBG ("ip4_addr_%s: si:%U %U/%u", is_del ? "del" : "add",
278 : format_vnet_sw_if_index_name, vnet_get_main (),
279 : sw_if_index, format_ip4_address, address, address_length);
280 :
281 0 : lip = lcp_itf_pair_get (lcp_itf_pair_find_by_phy (sw_if_index));
282 0 : if (!lip)
283 0 : return;
284 :
285 0 : if (lip->lip_namespace)
286 : {
287 0 : curr_ns_fd = clib_netns_open (NULL /* self */);
288 0 : vif_ns_fd = clib_netns_open (lip->lip_namespace);
289 0 : if (vif_ns_fd != -1)
290 0 : clib_setns (vif_ns_fd);
291 : }
292 :
293 0 : LCP_ITF_PAIR_DBG ("ip4_addr_%s: %U ip4 %U/%u", is_del ? "del" : "add",
294 : format_lcp_itf_pair, lip, format_ip4_address, address,
295 : address_length);
296 :
297 0 : if (is_del)
298 0 : vnet_netlink_del_ip4_addr (lip->lip_vif_index, address, address_length);
299 : else
300 0 : vnet_netlink_add_ip4_addr (lip->lip_vif_index, address, address_length);
301 :
302 0 : if (vif_ns_fd != -1)
303 0 : close (vif_ns_fd);
304 :
305 0 : if (curr_ns_fd != -1)
306 : {
307 0 : clib_setns (curr_ns_fd);
308 0 : close (curr_ns_fd);
309 : }
310 0 : return;
311 : }
312 :
313 : static void
314 12 : lcp_itf_ip6_add_del_interface_addr (ip6_main_t *im, uword opaque,
315 : u32 sw_if_index, ip6_address_t *address,
316 : u32 address_length, u32 if_address_index,
317 : u32 is_del)
318 : {
319 : const lcp_itf_pair_t *lip;
320 12 : int curr_ns_fd = -1;
321 12 : int vif_ns_fd = -1;
322 :
323 12 : if (!lcp_sync ())
324 12 : return;
325 :
326 0 : LCP_ITF_PAIR_DBG ("ip6_addr_%s: si:%U %U/%u", is_del ? "del" : "add",
327 : format_vnet_sw_if_index_name, vnet_get_main (),
328 : sw_if_index, format_ip6_address, address, address_length);
329 :
330 0 : lip = lcp_itf_pair_get (lcp_itf_pair_find_by_phy (sw_if_index));
331 0 : if (!lip)
332 0 : return;
333 :
334 0 : if (lip->lip_namespace)
335 : {
336 0 : curr_ns_fd = clib_netns_open (NULL /* self */);
337 0 : vif_ns_fd = clib_netns_open (lip->lip_namespace);
338 0 : if (vif_ns_fd != -1)
339 0 : clib_setns (vif_ns_fd);
340 : }
341 0 : LCP_ITF_PAIR_DBG ("ip6_addr_%s: %U ip4 %U/%u", is_del ? "del" : "add",
342 : format_lcp_itf_pair, lip, format_ip6_address, address,
343 : address_length);
344 0 : if (is_del)
345 0 : vnet_netlink_del_ip6_addr (lip->lip_vif_index, address, address_length);
346 : else
347 0 : vnet_netlink_add_ip6_addr (lip->lip_vif_index, address, address_length);
348 :
349 0 : if (vif_ns_fd != -1)
350 0 : close (vif_ns_fd);
351 :
352 0 : if (curr_ns_fd != -1)
353 : {
354 0 : clib_setns (curr_ns_fd);
355 0 : close (curr_ns_fd);
356 : }
357 : }
358 :
359 : static clib_error_t *
360 19 : lcp_itf_interface_add_del (vnet_main_t *vnm, u32 sw_if_index, u32 is_create)
361 : {
362 : const vnet_sw_interface_t *sw;
363 : uword is_sub;
364 :
365 19 : if (!lcp_auto_subint ())
366 19 : return NULL;
367 :
368 0 : sw = vnet_get_sw_interface_or_null (vnm, sw_if_index);
369 0 : if (!sw)
370 0 : return NULL;
371 :
372 0 : is_sub = vnet_sw_interface_is_sub (vnm, sw_if_index);
373 0 : if (!is_sub)
374 0 : return NULL;
375 :
376 0 : LCP_ITF_PAIR_DBG ("interface_%s: sw %U parent %U", is_create ? "add" : "del",
377 : format_vnet_sw_if_index_name, vnet_get_main (),
378 : sw->sw_if_index, format_vnet_sw_if_index_name,
379 : vnet_get_main (), sw->sup_sw_if_index);
380 :
381 0 : if (is_create)
382 : {
383 : const lcp_itf_pair_t *sup_lip;
384 0 : u8 *name = 0;
385 :
386 : // If the parent has a LIP auto-create a LIP for this interface
387 : sup_lip =
388 0 : lcp_itf_pair_get (lcp_itf_pair_find_by_phy (sw->sup_sw_if_index));
389 0 : if (!sup_lip)
390 0 : return NULL;
391 :
392 0 : name = format (name, "%s.%d%c", sup_lip->lip_host_name, sw->sub.id, 0);
393 :
394 0 : LCP_ITF_PAIR_INFO (
395 : "interface_%s: %U has parent %U, auto-creating LCP with host-if %s",
396 : is_create ? "add" : "del", format_vnet_sw_if_index_name,
397 : vnet_get_main (), sw->sw_if_index, format_lcp_itf_pair, sup_lip, name);
398 :
399 0 : lcp_itf_pair_create (sw->sw_if_index, name, LCP_ITF_HOST_TAP,
400 : sup_lip->lip_namespace, NULL);
401 :
402 0 : vec_free (name);
403 : }
404 : else
405 : {
406 0 : lcp_itf_pair_delete (sw_if_index);
407 : }
408 :
409 0 : return NULL;
410 : }
411 :
412 6 : VNET_SW_INTERFACE_ADD_DEL_FUNCTION (lcp_itf_interface_add_del);
413 :
414 : static clib_error_t *
415 2 : lcp_itf_sync_init (vlib_main_t *vm)
416 : {
417 2 : ip4_main_t *im4 = &ip4_main;
418 2 : ip6_main_t *im6 = &ip6_main;
419 :
420 : ip4_add_del_interface_address_callback_t cb4;
421 : ip6_add_del_interface_address_callback_t cb6;
422 :
423 2 : cb4.function = lcp_itf_ip4_add_del_interface_addr;
424 2 : cb4.function_opaque = 0;
425 2 : vec_add1 (im4->add_del_interface_address_callbacks, cb4);
426 :
427 2 : cb6.function = lcp_itf_ip6_add_del_interface_addr;
428 2 : cb6.function_opaque = 0;
429 2 : vec_add1 (im6->add_del_interface_address_callbacks, cb6);
430 :
431 2 : return NULL;
432 : }
433 :
434 4 : VLIB_INIT_FUNCTION (lcp_itf_sync_init) = {
435 : .runs_after = VLIB_INITS ("vnet_interface_init", "tcp_init", "udp_init"),
436 : };
437 :
438 : /*
439 : * fd.io coding-style-patch-verification: ON
440 : *
441 : * Local Variables:
442 : * eval: (c-set-style "gnu")
443 : * End:
444 : */
|