Neale Ranns | 59f7113 | 2020-04-08 12:19:38 +0000 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (c) 2020 Cisco and/or its affiliates. |
| 3 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | * you may not use this file except in compliance with the License. |
| 5 | * You may obtain a copy of the License at: |
| 6 | * |
| 7 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | * |
| 9 | * Unless required by applicable law or agreed to in writing, software |
| 10 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | * See the License for the specific language governing permissions and |
| 13 | * limitations under the License. |
| 14 | */ |
| 15 | |
| 16 | #include <vnet/ip/ip.h> |
| 17 | |
| 18 | /** |
| 19 | * @file |
| 20 | * @brief IP prefix management on interfaces |
| 21 | */ |
| 22 | |
| 23 | u32 |
| 24 | ip_interface_address_find (ip_lookup_main_t * lm, |
| 25 | void *addr_fib, u32 address_length) |
| 26 | { |
| 27 | uword *p = mhash_get (&lm->address_to_if_address_index, addr_fib); |
| 28 | |
| 29 | if (p) |
| 30 | return (p[0]); |
| 31 | |
| 32 | return (~0); |
| 33 | } |
| 34 | |
| 35 | clib_error_t * |
| 36 | ip_interface_address_add (ip_lookup_main_t * lm, |
| 37 | u32 sw_if_index, |
| 38 | void *addr_fib, |
| 39 | u32 address_length, u32 * result_if_address_index) |
| 40 | { |
| 41 | vnet_main_t *vnm = vnet_get_main (); |
| 42 | ip_interface_address_t *a, *prev; |
| 43 | u32 pi; /* previous index */ |
| 44 | u32 ai; |
| 45 | u32 hi; /* head index */ |
| 46 | |
| 47 | /* Verify given length. */ |
| 48 | if ((address_length == 0) || |
| 49 | (lm->is_ip6 && address_length > 128) || |
| 50 | (!lm->is_ip6 && address_length > 32)) |
| 51 | { |
| 52 | vnm->api_errno = VNET_API_ERROR_ADDRESS_LENGTH_MISMATCH; |
| 53 | return clib_error_create |
| 54 | ("%U wrong length for interface %U", |
| 55 | lm->format_address_and_length, addr_fib, |
| 56 | address_length, format_vnet_sw_if_index_name, vnm, sw_if_index); |
| 57 | } |
| 58 | |
| 59 | vec_validate_init_empty (lm->if_address_pool_index_by_sw_if_index, |
| 60 | sw_if_index, ~0); |
| 61 | |
| 62 | pool_get_zero (lm->if_address_pool, a); |
| 63 | |
| 64 | ai = a - lm->if_address_pool; |
| 65 | hi = pi = lm->if_address_pool_index_by_sw_if_index[sw_if_index]; |
| 66 | |
| 67 | prev = 0; |
| 68 | while (pi != (u32) ~ 0) |
| 69 | { |
| 70 | prev = pool_elt_at_index (lm->if_address_pool, pi); |
| 71 | pi = prev->next_this_sw_interface; |
| 72 | } |
| 73 | pi = prev ? prev - lm->if_address_pool : (u32) ~ 0; |
| 74 | |
| 75 | a->address_key = mhash_set (&lm->address_to_if_address_index, |
| 76 | addr_fib, ai, /* old_value */ 0); |
| 77 | a->address_length = address_length; |
| 78 | a->sw_if_index = sw_if_index; |
| 79 | a->flags = 0; |
| 80 | a->prev_this_sw_interface = pi; |
| 81 | a->next_this_sw_interface = ~0; |
| 82 | if (prev) |
| 83 | prev->next_this_sw_interface = ai; |
| 84 | |
| 85 | lm->if_address_pool_index_by_sw_if_index[sw_if_index] = |
| 86 | (hi != ~0) ? hi : ai; |
| 87 | |
| 88 | *result_if_address_index = ai; |
| 89 | |
| 90 | return (NULL); |
| 91 | } |
| 92 | |
| 93 | void |
| 94 | ip_interface_address_del (ip_lookup_main_t * lm, |
| 95 | u32 address_index, void *addr_fib) |
| 96 | { |
| 97 | ip_interface_address_t *a, *prev, *next; |
| 98 | |
| 99 | a = pool_elt_at_index (lm->if_address_pool, address_index); |
| 100 | |
| 101 | if (a->prev_this_sw_interface != ~0) |
| 102 | { |
| 103 | prev = pool_elt_at_index (lm->if_address_pool, |
| 104 | a->prev_this_sw_interface); |
| 105 | prev->next_this_sw_interface = a->next_this_sw_interface; |
| 106 | } |
| 107 | if (a->next_this_sw_interface != ~0) |
| 108 | { |
| 109 | next = pool_elt_at_index (lm->if_address_pool, |
| 110 | a->next_this_sw_interface); |
| 111 | next->prev_this_sw_interface = a->prev_this_sw_interface; |
| 112 | |
| 113 | if (a->prev_this_sw_interface == ~0) |
| 114 | lm->if_address_pool_index_by_sw_if_index[a->sw_if_index] = |
| 115 | a->next_this_sw_interface; |
| 116 | } |
| 117 | |
| 118 | if ((a->next_this_sw_interface == ~0) && (a->prev_this_sw_interface == ~0)) |
| 119 | lm->if_address_pool_index_by_sw_if_index[a->sw_if_index] = ~0; |
| 120 | |
| 121 | mhash_unset (&lm->address_to_if_address_index, addr_fib, |
| 122 | /* old_value */ 0); |
| 123 | pool_put (lm->if_address_pool, a); |
| 124 | } |
| 125 | |
| 126 | u8 |
| 127 | ip_interface_has_address (u32 sw_if_index, ip46_address_t * ip, u8 is_ip4) |
| 128 | { |
| 129 | ip_interface_address_t *ia = 0; |
| 130 | |
| 131 | if (is_ip4) |
| 132 | { |
| 133 | ip_lookup_main_t *lm4 = &ip4_main.lookup_main; |
| 134 | ip4_address_t *ip4; |
| 135 | /* *INDENT-OFF* */ |
| 136 | foreach_ip_interface_address (lm4, ia, sw_if_index, 1 /* unnumbered */ , |
| 137 | ({ |
| 138 | ip4 = ip_interface_address_get_address (lm4, ia); |
| 139 | if (ip4_address_compare (ip4, &ip->ip4) == 0) |
| 140 | return 1; |
| 141 | })); |
| 142 | /* *INDENT-ON* */ |
| 143 | } |
| 144 | else |
| 145 | { |
| 146 | ip_lookup_main_t *lm6 = &ip6_main.lookup_main; |
| 147 | ip6_address_t *ip6; |
| 148 | /* *INDENT-OFF* */ |
| 149 | foreach_ip_interface_address (lm6, ia, sw_if_index, 1 /* unnumbered */ , |
| 150 | ({ |
| 151 | ip6 = ip_interface_address_get_address (lm6, ia); |
| 152 | if (ip6_address_compare (ip6, &ip->ip6) == 0) |
| 153 | return 1; |
| 154 | })); |
| 155 | /* *INDENT-ON* */ |
| 156 | } |
| 157 | return 0; |
| 158 | } |
| 159 | |
| 160 | void * |
| 161 | ip_interface_get_first_ip (u32 sw_if_index, u8 is_ip4) |
| 162 | { |
| 163 | ip_lookup_main_t *lm4 = &ip4_main.lookup_main; |
| 164 | ip_lookup_main_t *lm6 = &ip6_main.lookup_main; |
| 165 | ip_interface_address_t *ia = 0; |
| 166 | |
| 167 | if (is_ip4) |
| 168 | { |
| 169 | /* *INDENT-OFF* */ |
| 170 | foreach_ip_interface_address (lm4, ia, sw_if_index, 1 /* unnumbered */ , |
| 171 | ({ |
| 172 | return ip_interface_address_get_address (lm4, ia); |
| 173 | })); |
| 174 | /* *INDENT-ON* */ |
| 175 | } |
| 176 | else |
| 177 | { |
| 178 | /* *INDENT-OFF* */ |
| 179 | foreach_ip_interface_address (lm6, ia, sw_if_index, 1 /* unnumbered */ , |
| 180 | ({ |
| 181 | ip6_address_t *rv; |
| 182 | rv = ip_interface_address_get_address (lm6, ia); |
| 183 | /* Trying to use a link-local ip6 src address is a fool's errand */ |
| 184 | if (!ip6_address_is_link_local_unicast (rv)) |
| 185 | return rv; |
| 186 | })); |
| 187 | /* *INDENT-ON* */ |
| 188 | } |
| 189 | |
| 190 | return 0; |
| 191 | } |
| 192 | |
| 193 | static walk_rc_t |
| 194 | ip_interface_address_mark_one_interface (vnet_main_t * vnm, |
| 195 | vnet_sw_interface_t * si, void *ctx) |
| 196 | { |
| 197 | ip_lookup_main_t *lm4 = &ip4_main.lookup_main; |
| 198 | ip_lookup_main_t *lm6 = &ip6_main.lookup_main; |
| 199 | ip_interface_address_t *ia = 0; |
| 200 | |
| 201 | /* *INDENT-OFF* */ |
| 202 | foreach_ip_interface_address (lm4, ia, si->sw_if_index, 1 /* unnumbered */ , |
| 203 | ({ |
| 204 | ia->flags |= IP_INTERFACE_ADDRESS_FLAG_STALE; |
| 205 | })); |
| 206 | foreach_ip_interface_address (lm6, ia, si->sw_if_index, 1 /* unnumbered */ , |
| 207 | ({ |
| 208 | ia->flags |= IP_INTERFACE_ADDRESS_FLAG_STALE; |
| 209 | })); |
| 210 | /* *INDENT-ON* */ |
| 211 | |
| 212 | return (WALK_CONTINUE); |
| 213 | } |
| 214 | |
| 215 | void |
| 216 | ip_interface_address_mark (void) |
| 217 | { |
| 218 | vnet_sw_interface_walk (vnet_get_main (), |
| 219 | ip_interface_address_mark_one_interface, NULL); |
| 220 | } |
| 221 | |
| 222 | static walk_rc_t |
| 223 | ip_interface_address_sweep_one_interface (vnet_main_t * vnm, |
| 224 | vnet_sw_interface_t * si, void *ctx) |
| 225 | { |
| 226 | vlib_main_t *vm = vlib_get_main (); |
| 227 | ip4_address_t *ip4_addrs = 0; |
| 228 | ip6_address_t *ip6_addrs = 0; |
| 229 | ip4_main_t *im4 = &ip4_main; |
| 230 | ip6_main_t *im6 = &ip6_main; |
| 231 | ip_interface_address_t *ia; |
| 232 | u32 *ip6_masks = 0; |
| 233 | u32 *ip4_masks = 0; |
| 234 | int i; |
| 235 | |
| 236 | /* *INDENT-OFF* */ |
| 237 | foreach_ip_interface_address (&im4->lookup_main, ia, si->sw_if_index, 1, |
| 238 | ({ |
| 239 | if (ia->flags & IP_INTERFACE_ADDRESS_FLAG_STALE) |
| 240 | { |
| 241 | ip4_address_t * x = (ip4_address_t *) |
| 242 | ip_interface_address_get_address (&im4->lookup_main, ia); |
| 243 | vec_add1 (ip4_addrs, x[0]); |
| 244 | vec_add1 (ip4_masks, ia->address_length); |
| 245 | } |
| 246 | })); |
| 247 | |
| 248 | foreach_ip_interface_address (&im6->lookup_main, ia, si->sw_if_index, 1, |
| 249 | ({ |
| 250 | if (ia->flags & IP_INTERFACE_ADDRESS_FLAG_STALE) |
| 251 | { |
| 252 | ip6_address_t * x = (ip6_address_t *) |
| 253 | ip_interface_address_get_address (&im6->lookup_main, ia); |
| 254 | vec_add1 (ip6_addrs, x[0]); |
| 255 | vec_add1 (ip6_masks, ia->address_length); |
| 256 | } |
| 257 | })); |
| 258 | /* *INDENT-ON* */ |
| 259 | |
| 260 | for (i = 0; i < vec_len (ip4_addrs); i++) |
| 261 | ip4_add_del_interface_address (vm, si->sw_if_index, &ip4_addrs[i], |
| 262 | ip4_masks[i], 1 /* is_del */ ); |
| 263 | for (i = 0; i < vec_len (ip6_addrs); i++) |
| 264 | ip6_add_del_interface_address (vm, si->sw_if_index, &ip6_addrs[i], |
| 265 | ip6_masks[i], 1 /* is_del */ ); |
| 266 | |
| 267 | vec_free (ip4_addrs); |
| 268 | vec_free (ip4_masks); |
| 269 | vec_free (ip6_addrs); |
| 270 | vec_free (ip6_masks); |
| 271 | |
| 272 | return (WALK_CONTINUE); |
| 273 | } |
| 274 | |
| 275 | void |
| 276 | ip_interface_address_sweep (void) |
| 277 | { |
| 278 | vnet_sw_interface_walk (vnet_get_main (), |
| 279 | ip_interface_address_sweep_one_interface, NULL); |
| 280 | } |
| 281 | |
| 282 | /* |
| 283 | * fd.io coding-style-patch-verification: ON |
| 284 | * |
| 285 | * Local Variables: |
| 286 | * eval: (c-set-style "gnu") |
| 287 | * End: |
| 288 | */ |