]>
Commit | Line | Data |
---|---|---|
7ef5a232 | 1 | /* Ethernet-VPN Packet and vty Processing File |
896014f4 DL |
2 | * Copyright (C) 2016 6WIND |
3 | * | |
4 | * This file is part of FRRouting. | |
5 | * | |
6 | * FRRouting is free software; you can redistribute it and/or modify it | |
7 | * under the terms of the GNU General Public License as published by the | |
8 | * Free Software Foundation; either version 2, or (at your option) any | |
9 | * later version. | |
10 | * | |
11 | * FRRouting is distributed in the hope that it will be useful, but | |
12 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | * General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU General Public License along | |
17 | * with this program; see the file COPYING; if not, write to the Free Software | |
18 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | |
19 | */ | |
7ef5a232 | 20 | |
7ef5a232 PG |
21 | #include <zebra.h> |
22 | ||
23 | #include "command.h" | |
24 | #include "filter.h" | |
25 | #include "prefix.h" | |
26 | #include "log.h" | |
27 | #include "memory.h" | |
28 | #include "stream.h" | |
14c1a7bf | 29 | #include "hash.h" |
30 | #include "jhash.h" | |
31 | #include "bitfield.h" | |
7ef5a232 PG |
32 | |
33 | #include "bgpd/bgp_attr_evpn.h" | |
34 | #include "bgpd/bgpd.h" | |
35 | #include "bgpd/bgp_table.h" | |
36 | #include "bgpd/bgp_route.h" | |
37 | #include "bgpd/bgp_attr.h" | |
38 | #include "bgpd/bgp_mplsvpn.h" | |
9bedbb1e | 39 | #include "bgpd/bgp_label.h" |
7ef5a232 | 40 | #include "bgpd/bgp_evpn.h" |
14c1a7bf | 41 | #include "bgpd/bgp_evpn_private.h" |
42 | #include "bgpd/bgp_ecommunity.h" | |
43 | ||
44 | /* | |
45 | * Private functions. | |
46 | */ | |
47 | ||
48 | /* | |
49 | * Make vni hash key. | |
50 | */ | |
51 | static unsigned int | |
52 | vni_hash_key_make(void *p) | |
53 | { | |
54 | struct bgpevpn *vpn = p; | |
55 | return (jhash_1word(vpn->vni, 0)); | |
56 | } | |
57 | ||
58 | /* | |
59 | * Comparison function for vni hash | |
60 | */ | |
61 | static int | |
62 | vni_hash_cmp (const void *p1, const void *p2) | |
63 | { | |
64 | const struct bgpevpn *vpn1 = p1; | |
65 | const struct bgpevpn *vpn2 = p2; | |
66 | ||
67 | if (!vpn1 && !vpn2) | |
68 | return 1; | |
69 | if (!vpn1 || !vpn2) | |
70 | return 0; | |
71 | return(vpn1->vni == vpn2->vni); | |
72 | } | |
73 | ||
74 | /* | |
75 | * Make import route target hash key. | |
76 | */ | |
77 | static unsigned int | |
78 | import_rt_hash_key_make (void *p) | |
79 | { | |
80 | struct irt_node *irt = p; | |
81 | char *pnt = irt->rt.val; | |
82 | unsigned int key = 0; | |
83 | int c=0; | |
84 | ||
85 | key += pnt[c]; | |
86 | key += pnt[c + 1]; | |
87 | key += pnt[c + 2]; | |
88 | key += pnt[c + 3]; | |
89 | key += pnt[c + 4]; | |
90 | key += pnt[c + 5]; | |
91 | key += pnt[c + 6]; | |
92 | key += pnt[c + 7]; | |
93 | ||
94 | return (key); | |
95 | } | |
96 | ||
97 | /* | |
98 | * Comparison function for import rt hash | |
99 | */ | |
100 | static int | |
101 | import_rt_hash_cmp (const void *p1, const void *p2) | |
102 | { | |
103 | const struct irt_node *irt1 = p1; | |
104 | const struct irt_node *irt2 = p2; | |
105 | ||
106 | if (irt1 == NULL && irt2 == NULL) | |
107 | return 1; | |
108 | ||
109 | if (irt1 == NULL || irt2 == NULL) | |
110 | return 0; | |
111 | ||
112 | return(memcmp(irt1->rt.val, irt2->rt.val, ECOMMUNITY_SIZE) == 0); | |
113 | } | |
114 | ||
7724c0a1 | 115 | /* |
116 | * Cleanup specific VNI upon EVPN (advertise-all-vni) being disabled. | |
117 | */ | |
118 | static void | |
119 | cleanup_vni_on_disable (struct hash_backet *backet, struct bgp *bgp) | |
120 | { | |
121 | } | |
122 | ||
14c1a7bf | 123 | /* |
124 | * Free a VNI entry; iterator function called during cleanup. | |
125 | */ | |
126 | static void | |
127 | free_vni_entry (struct hash_backet *backet, struct bgp *bgp) | |
128 | { | |
129 | } | |
130 | ||
131 | ||
132 | /* | |
133 | * Public functions. | |
134 | */ | |
7ef5a232 PG |
135 | |
136 | int | |
4d0e6ece PG |
137 | bgp_nlri_parse_evpn(struct peer *peer, struct attr *attr, |
138 | struct bgp_nlri *packet, int withdraw) | |
7ef5a232 | 139 | { |
4d0e6ece PG |
140 | u_char *pnt; |
141 | u_char *lim; | |
142 | struct prefix p; | |
143 | struct prefix_rd prd; | |
144 | struct evpn_addr *p_evpn_p; | |
145 | struct bgp_route_evpn evpn; | |
146 | uint8_t route_type, route_length; | |
9bedbb1e | 147 | mpls_label_t label; |
4d0e6ece PG |
148 | u_int32_t addpath_id = 0; |
149 | ||
150 | /* Check peer status. */ | |
151 | if (peer->status != Established) | |
152 | return 0; | |
153 | ||
154 | /* Make prefix_rd */ | |
155 | prd.family = AF_UNSPEC; | |
156 | prd.prefixlen = 64; | |
157 | ||
158 | p_evpn_p = &p.u.prefix_evpn; | |
159 | pnt = packet->nlri; | |
160 | lim = pnt + packet->length; | |
161 | while (pnt < lim) { | |
162 | /* clear evpn structure */ | |
163 | memset(&evpn, 0, sizeof(evpn)); | |
164 | ||
165 | /* Clear prefix structure. */ | |
166 | memset(&p, 0, sizeof(struct prefix)); | |
167 | memset(&evpn.gw_ip, 0, sizeof(union gw_addr)); | |
168 | memset(&evpn.eth_s_id, 0, sizeof(struct eth_segment_id)); | |
169 | ||
170 | /* Fetch Route Type */ | |
171 | route_type = *pnt++; | |
172 | route_length = *pnt++; | |
173 | /* simply ignore. goto next route type if any */ | |
174 | if (route_type != EVPN_IP_PREFIX) { | |
175 | if (pnt + route_length > lim) { | |
176 | zlog_err | |
177 | ("not enough bytes for New Route Type left in NLRI?"); | |
178 | return -1; | |
179 | } | |
180 | pnt += route_length; | |
181 | continue; | |
182 | } | |
183 | ||
184 | /* Fetch RD */ | |
185 | if (pnt + 8 > lim) { | |
186 | zlog_err("not enough bytes for RD left in NLRI?"); | |
187 | return -1; | |
188 | } | |
189 | ||
190 | /* Copy routing distinguisher to rd. */ | |
191 | memcpy(&prd.val, pnt, 8); | |
192 | pnt += 8; | |
193 | ||
194 | /* Fetch ESI */ | |
195 | if (pnt + 10 > lim) { | |
196 | zlog_err("not enough bytes for ESI left in NLRI?"); | |
197 | return -1; | |
198 | } | |
199 | memcpy(&evpn.eth_s_id.val, pnt, 10); | |
200 | pnt += 10; | |
201 | ||
202 | /* Fetch Ethernet Tag */ | |
203 | if (pnt + 4 > lim) { | |
204 | zlog_err("not enough bytes for Eth Tag left in NLRI?"); | |
205 | return -1; | |
206 | } | |
207 | ||
208 | if (route_type == EVPN_IP_PREFIX) { | |
209 | p_evpn_p->route_type = route_type; | |
210 | memcpy(&(p_evpn_p->eth_tag), pnt, 4); | |
211 | p_evpn_p->eth_tag = ntohl(p_evpn_p->eth_tag); | |
212 | pnt += 4; | |
213 | ||
214 | /* Fetch IP prefix length. */ | |
215 | p_evpn_p->ip_prefix_length = *pnt++; | |
216 | ||
217 | if (p_evpn_p->ip_prefix_length > 128) { | |
218 | zlog_err("invalid prefixlen %d in EVPN NLRI?", | |
219 | p.prefixlen); | |
220 | return -1; | |
221 | } | |
222 | /* determine IPv4 or IPv6 prefix */ | |
223 | if (route_length - 4 - 10 - 8 - | |
224 | 3 /* label to be read */ >= 32) { | |
86f1ef44 | 225 | SET_IPADDR_V6 (&p_evpn_p->ip); |
226 | memcpy(&(p_evpn_p->ip.ipaddr_v6), pnt, 16); | |
4d0e6ece PG |
227 | pnt += 16; |
228 | memcpy(&evpn.gw_ip.ipv6, pnt, 16); | |
229 | pnt += 16; | |
230 | } else { | |
86f1ef44 | 231 | SET_IPADDR_V4 (&p_evpn_p->ip); |
232 | memcpy(&(p_evpn_p->ip.ipaddr_v4), pnt, 4); | |
4d0e6ece PG |
233 | pnt += 4; |
234 | memcpy(&evpn.gw_ip.ipv4, pnt, 4); | |
235 | pnt += 4; | |
236 | } | |
237 | p.family = AFI_L2VPN; | |
86f1ef44 | 238 | if (IS_IPADDR_V4(&p_evpn_p->ip)) |
4d0e6ece PG |
239 | p.prefixlen = |
240 | (u_char) PREFIX_LEN_ROUTE_TYPE_5_IPV4; | |
241 | else | |
242 | p.prefixlen = PREFIX_LEN_ROUTE_TYPE_5_IPV6; | |
243 | p.family = AF_ETHERNET; | |
244 | } | |
245 | ||
246 | /* Fetch Label */ | |
9bedbb1e | 247 | if (pnt + BGP_LABEL_BYTES > lim) { |
4d0e6ece PG |
248 | zlog_err("not enough bytes for Label left in NLRI?"); |
249 | return -1; | |
250 | } | |
4d0e6ece | 251 | |
9bedbb1e DW |
252 | memcpy(&label, pnt, BGP_LABEL_BYTES); |
253 | bgp_set_valid_label(&label); | |
254 | pnt += BGP_LABEL_BYTES; | |
4d0e6ece PG |
255 | |
256 | if (!withdraw) { | |
257 | bgp_update(peer, &p, addpath_id, attr, AFI_L2VPN, | |
258 | SAFI_EVPN, ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, | |
9bedbb1e | 259 | &prd, &label, 0, &evpn); |
4d0e6ece PG |
260 | } else { |
261 | bgp_withdraw(peer, &p, addpath_id, attr, AFI_L2VPN, | |
262 | SAFI_EVPN, ZEBRA_ROUTE_BGP, | |
9bedbb1e | 263 | BGP_ROUTE_NORMAL, &prd, &label, &evpn); |
4d0e6ece | 264 | } |
7ef5a232 PG |
265 | } |
266 | ||
4d0e6ece PG |
267 | /* Packet length consistency check. */ |
268 | if (pnt != lim) | |
269 | return -1; | |
270 | return 0; | |
7ef5a232 | 271 | } |
b18825eb PG |
272 | |
273 | void | |
4d0e6ece PG |
274 | bgp_packet_mpattr_route_type_5(struct stream *s, |
275 | struct prefix *p, struct prefix_rd *prd, | |
9bedbb1e | 276 | mpls_label_t *label, struct attr *attr) |
b18825eb | 277 | { |
4d0e6ece PG |
278 | int len; |
279 | char temp[16]; | |
280 | struct evpn_addr *p_evpn_p; | |
281 | ||
282 | memset(&temp, 0, 16); | |
283 | if (p->family != AF_ETHERNET) | |
284 | return; | |
285 | p_evpn_p = &(p->u.prefix_evpn); | |
86f1ef44 | 286 | if (IS_IPADDR_V4(&p_evpn_p->ip)) |
4d0e6ece PG |
287 | len = 8; /* ipv4 */ |
288 | else | |
289 | len = 32; /* ipv6 */ | |
290 | stream_putc(s, EVPN_IP_PREFIX); | |
291 | stream_putc(s, | |
292 | 8 /* RD */ + 10 /* ESI */ + 4 /* EthTag */ + 1 + len + | |
293 | 3 /* label */ ); | |
294 | stream_put(s, prd->val, 8); | |
295 | if (attr && attr->extra) | |
296 | stream_put(s, &(attr->extra->evpn_overlay.eth_s_id), 10); | |
297 | else | |
298 | stream_put(s, &temp, 10); | |
299 | stream_putl(s, p_evpn_p->eth_tag); | |
300 | stream_putc(s, p_evpn_p->ip_prefix_length); | |
86f1ef44 | 301 | if (IS_IPADDR_V4(&p_evpn_p->ip)) |
302 | stream_put_ipv4(s, p_evpn_p->ip.ipaddr_v4.s_addr); | |
4d0e6ece | 303 | else |
86f1ef44 | 304 | stream_put(s, &p_evpn_p->ip.ipaddr_v6, 16); |
4d0e6ece | 305 | if (attr && attr->extra) { |
86f1ef44 | 306 | if (IS_IPADDR_V4(&p_evpn_p->ip)) |
4d0e6ece PG |
307 | stream_put_ipv4(s, |
308 | attr->extra->evpn_overlay.gw_ip.ipv4. | |
309 | s_addr); | |
310 | else | |
311 | stream_put(s, &(attr->extra->evpn_overlay.gw_ip.ipv6), | |
312 | 16); | |
313 | } else { | |
86f1ef44 | 314 | if (IS_IPADDR_V4(&p_evpn_p->ip)) |
4d0e6ece PG |
315 | stream_put_ipv4(s, 0); |
316 | else | |
317 | stream_put(s, &temp, 16); | |
318 | } | |
319 | if (label) | |
320 | stream_put(s, label, 3); | |
321 | else | |
322 | stream_put3(s, 0); | |
323 | return; | |
b18825eb | 324 | } |
14c1a7bf | 325 | |
7724c0a1 | 326 | /* |
327 | * Cleanup EVPN information on disable - Need to delete and withdraw | |
328 | * EVPN routes from peers. | |
329 | */ | |
330 | void | |
331 | bgp_evpn_cleanup_on_disable (struct bgp *bgp) | |
332 | { | |
333 | hash_iterate (bgp->vnihash, | |
334 | (void (*) (struct hash_backet *, void *)) | |
335 | cleanup_vni_on_disable, bgp); | |
336 | } | |
337 | ||
14c1a7bf | 338 | /* |
339 | * Cleanup EVPN information - invoked at the time of bgpd exit or when the | |
340 | * BGP instance (default) is being freed. | |
341 | */ | |
342 | void | |
343 | bgp_evpn_cleanup (struct bgp *bgp) | |
344 | { | |
345 | hash_iterate (bgp->vnihash, | |
346 | (void (*) (struct hash_backet *, void *)) | |
347 | free_vni_entry, bgp); | |
348 | hash_free (bgp->import_rt_hash); | |
349 | bgp->import_rt_hash = NULL; | |
350 | hash_free (bgp->vnihash); | |
351 | bgp->vnihash = NULL; | |
352 | bf_free (bgp->rd_idspace); | |
353 | } | |
354 | ||
355 | /* | |
356 | * Initialization for EVPN | |
357 | * Create | |
358 | * VNI hash table | |
359 | * hash for RT to VNI | |
360 | * unique rd id space for auto derivation of RD for VNIs | |
361 | */ | |
362 | void | |
363 | bgp_evpn_init (struct bgp *bgp) | |
364 | { | |
365 | bgp->vnihash = hash_create (vni_hash_key_make, | |
366 | vni_hash_cmp, | |
367 | "BGP VNI Hash"); | |
368 | bgp->import_rt_hash = hash_create (import_rt_hash_key_make, | |
369 | import_rt_hash_cmp, | |
370 | "BGP Import RT Hash"); | |
371 | bf_init (bgp->rd_idspace, UINT16_MAX); | |
372 | /*assign 0th index in the bitfield, so that we start with id 1*/ | |
373 | bf_assign_zero_index (bgp->rd_idspace); | |
374 | } |