/src/frr/bgpd/bgp_attr_evpn.c
Line | Count | Source (jump to first uncovered line) |
1 | | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | | /* Ethernet-VPN Attribute handling file |
3 | | * Copyright (C) 2016 6WIND |
4 | | */ |
5 | | |
6 | | #include <zebra.h> |
7 | | |
8 | | #include "command.h" |
9 | | #include "filter.h" |
10 | | #include "prefix.h" |
11 | | #include "log.h" |
12 | | #include "memory.h" |
13 | | #include "stream.h" |
14 | | #include "vxlan.h" |
15 | | |
16 | | #include "bgpd/bgpd.h" |
17 | | #include "bgpd/bgp_attr.h" |
18 | | #include "bgpd/bgp_route.h" |
19 | | #include "bgpd/bgp_attr_evpn.h" |
20 | | #include "bgpd/bgp_ecommunity.h" |
21 | | #include "bgpd/bgp_evpn.h" |
22 | | #include "bgpd/bgp_evpn_private.h" |
23 | | |
24 | | bool bgp_route_evpn_same(const struct bgp_route_evpn *e1, |
25 | | const struct bgp_route_evpn *e2) |
26 | 0 | { |
27 | 0 | return (e1->type == e2->type && |
28 | 0 | !memcmp(&(e1->eth_s_id), &(e2->eth_s_id), sizeof(esi_t)) && |
29 | 0 | !ipaddr_cmp(&(e1->gw_ip), &(e2->gw_ip))); |
30 | 0 | } |
31 | | |
32 | | void bgp_add_routermac_ecom(struct attr *attr, struct ethaddr *routermac) |
33 | 0 | { |
34 | 0 | struct ecommunity_val routermac_ecom; |
35 | 0 | struct ecommunity *ecomm = bgp_attr_get_ecommunity(attr); |
36 | |
|
37 | 0 | memset(&routermac_ecom, 0, sizeof(routermac_ecom)); |
38 | 0 | routermac_ecom.val[0] = ECOMMUNITY_ENCODE_EVPN; |
39 | 0 | routermac_ecom.val[1] = ECOMMUNITY_EVPN_SUBTYPE_ROUTERMAC; |
40 | 0 | memcpy(&routermac_ecom.val[2], routermac->octet, ETH_ALEN); |
41 | 0 | if (!ecomm) { |
42 | 0 | bgp_attr_set_ecommunity(attr, ecommunity_new()); |
43 | 0 | ecomm = bgp_attr_get_ecommunity(attr); |
44 | 0 | } |
45 | 0 | ecommunity_add_val(ecomm, &routermac_ecom, false, false); |
46 | 0 | ecommunity_str(ecomm); |
47 | 0 | } |
48 | | |
49 | | /* converts to an esi |
50 | | * returns 1 on success, 0 otherwise |
51 | | * format accepted: AA:BB:CC:DD:EE:FF:GG:HH:II:JJ |
52 | | * if id is null, check only is done |
53 | | */ |
54 | | bool str2esi(const char *str, esi_t *id) |
55 | 0 | { |
56 | 0 | unsigned int a[ESI_BYTES]; |
57 | 0 | int i; |
58 | |
|
59 | 0 | if (!str) |
60 | 0 | return false; |
61 | 0 | if (sscanf(str, "%2x:%2x:%2x:%2x:%2x:%2x:%2x:%2x:%2x:%2x", a + 0, a + 1, |
62 | 0 | a + 2, a + 3, a + 4, a + 5, a + 6, a + 7, a + 8, a + 9) |
63 | 0 | != ESI_BYTES) { |
64 | | /* error in incoming str length */ |
65 | 0 | return false; |
66 | 0 | } |
67 | | /* valid mac address */ |
68 | 0 | if (!id) |
69 | 0 | return true; |
70 | 0 | for (i = 0; i < ESI_BYTES; ++i) |
71 | 0 | id->val[i] = a[i] & 0xff; |
72 | 0 | return true; |
73 | 0 | } |
74 | | |
75 | | char *ecom_mac2str(char *ecom_mac) |
76 | 0 | { |
77 | 0 | char *en; |
78 | |
|
79 | 0 | en = ecom_mac; |
80 | 0 | en += 2; |
81 | |
|
82 | 0 | return prefix_mac2str((struct ethaddr *)en, NULL, 0); |
83 | 0 | } |
84 | | |
85 | | /* Fetch router-mac from extended community */ |
86 | | bool bgp_attr_rmac(struct attr *attr, struct ethaddr *rmac) |
87 | 107 | { |
88 | 107 | uint32_t i = 0; |
89 | 107 | struct ecommunity *ecom; |
90 | | |
91 | 107 | ecom = bgp_attr_get_ecommunity(attr); |
92 | 107 | if (!ecom || !ecom->size) |
93 | 0 | return false; |
94 | | |
95 | | /* If there is a router mac extended community, set RMAC in attr */ |
96 | 10.4k | for (i = 0; i < ecom->size; i++) { |
97 | 10.3k | uint8_t *pnt = NULL; |
98 | 10.3k | uint8_t type = 0; |
99 | 10.3k | uint8_t sub_type = 0; |
100 | | |
101 | 10.3k | pnt = (ecom->val + (i * ECOMMUNITY_SIZE)); |
102 | 10.3k | type = *pnt++; |
103 | 10.3k | sub_type = *pnt++; |
104 | | |
105 | 10.3k | if (!(type == ECOMMUNITY_ENCODE_EVPN |
106 | 10.3k | && sub_type == ECOMMUNITY_EVPN_SUBTYPE_ROUTERMAC)) |
107 | 10.3k | continue; |
108 | | |
109 | 4 | memcpy(rmac, pnt, ETH_ALEN); |
110 | 4 | return true; |
111 | 10.3k | } |
112 | 103 | return false; |
113 | 107 | } |
114 | | |
115 | | /* |
116 | | * return true if attr contains default gw extended community |
117 | | */ |
118 | | uint8_t bgp_attr_default_gw(struct attr *attr) |
119 | 107 | { |
120 | 107 | struct ecommunity *ecom; |
121 | 107 | uint32_t i; |
122 | | |
123 | 107 | ecom = bgp_attr_get_ecommunity(attr); |
124 | 107 | if (!ecom || !ecom->size) |
125 | 0 | return 0; |
126 | | |
127 | | /* If there is a default gw extendd community return true otherwise |
128 | | * return 0 */ |
129 | 10.7k | for (i = 0; i < ecom->size; i++) { |
130 | 10.6k | uint8_t *pnt; |
131 | 10.6k | uint8_t type, sub_type; |
132 | | |
133 | 10.6k | pnt = (ecom->val + (i * ECOMMUNITY_SIZE)); |
134 | 10.6k | type = *pnt++; |
135 | 10.6k | sub_type = *pnt++; |
136 | | |
137 | 10.6k | if ((type == ECOMMUNITY_ENCODE_OPAQUE |
138 | 10.6k | && sub_type == ECOMMUNITY_EVPN_SUBTYPE_DEF_GW)) |
139 | 1 | return 1; |
140 | 10.6k | } |
141 | | |
142 | 106 | return 0; |
143 | 107 | } |
144 | | |
145 | | /* |
146 | | * Fetch and return the DF preference and algorithm from |
147 | | * DF election extended community, if present, else 0. |
148 | | */ |
149 | | uint16_t bgp_attr_df_pref_from_ec(struct attr *attr, uint8_t *alg) |
150 | 107 | { |
151 | 107 | struct ecommunity *ecom; |
152 | 107 | uint32_t i; |
153 | 107 | uint16_t df_pref = 0; |
154 | | |
155 | 107 | *alg = EVPN_MH_DF_ALG_SERVICE_CARVING; |
156 | 107 | ecom = bgp_attr_get_ecommunity(attr); |
157 | 107 | if (!ecom || !ecom->size) |
158 | 0 | return 0; |
159 | | |
160 | 7.82k | for (i = 0; i < ecom->size; i++) { |
161 | 7.75k | uint8_t *pnt; |
162 | 7.75k | uint8_t type, sub_type; |
163 | | |
164 | 7.75k | pnt = (ecom->val + (i * ECOMMUNITY_SIZE)); |
165 | 7.75k | type = *pnt++; |
166 | 7.75k | sub_type = *pnt++; |
167 | 7.75k | if (!(type == ECOMMUNITY_ENCODE_EVPN |
168 | 7.75k | && sub_type == ECOMMUNITY_EVPN_SUBTYPE_DF_ELECTION)) |
169 | 7.72k | continue; |
170 | | |
171 | 31 | *alg = (*pnt++) & ECOMMUNITY_EVPN_SUBTYPE_DF_ALG_BITS; |
172 | | |
173 | 31 | pnt += 3; |
174 | 31 | pnt = ptr_get_be16(pnt, &df_pref); |
175 | 31 | (void)pnt; /* consume value */ |
176 | 31 | break; |
177 | 7.75k | } |
178 | | |
179 | 107 | return df_pref; |
180 | 107 | } |
181 | | |
182 | | /* |
183 | | * Fetch and return the sequence number from MAC Mobility extended |
184 | | * community, if present, else 0. |
185 | | */ |
186 | | uint32_t bgp_attr_mac_mobility_seqnum(struct attr *attr, uint8_t *sticky) |
187 | 107 | { |
188 | 107 | struct ecommunity *ecom; |
189 | 107 | uint32_t i; |
190 | 107 | uint8_t flags = 0; |
191 | | |
192 | 107 | ecom = bgp_attr_get_ecommunity(attr); |
193 | 107 | if (!ecom || !ecom->size) |
194 | 0 | return 0; |
195 | | |
196 | | /* If there is a MAC Mobility extended community, return its |
197 | | * sequence number. |
198 | | * TODO: RFC is silent on handling of multiple MAC mobility extended |
199 | | * communities for the same route. We will bail out upon the first |
200 | | * one. |
201 | | */ |
202 | 8.58k | for (i = 0; i < ecom->size; i++) { |
203 | 8.50k | const uint8_t *pnt; |
204 | 8.50k | uint8_t type, sub_type; |
205 | 8.50k | uint32_t seq_num; |
206 | | |
207 | 8.50k | pnt = (ecom->val + (i * ECOMMUNITY_SIZE)); |
208 | 8.50k | type = *pnt++; |
209 | 8.50k | sub_type = *pnt++; |
210 | 8.50k | if (!(type == ECOMMUNITY_ENCODE_EVPN |
211 | 8.50k | && sub_type == ECOMMUNITY_EVPN_SUBTYPE_MACMOBILITY)) |
212 | 8.48k | continue; |
213 | 25 | flags = *pnt++; |
214 | | |
215 | 25 | if (flags & ECOMMUNITY_EVPN_SUBTYPE_MACMOBILITY_FLAG_STICKY) |
216 | 3 | *sticky = 1; |
217 | 22 | else |
218 | 22 | *sticky = 0; |
219 | | |
220 | 25 | pnt++; |
221 | 25 | pnt = ptr_get_be32(pnt, &seq_num); |
222 | 25 | (void)pnt; /* consume value */ |
223 | 25 | return seq_num; |
224 | 8.50k | } |
225 | | |
226 | 82 | return 0; |
227 | 107 | } |
228 | | |
229 | | /* |
230 | | * return true if attr contains router flag extended community |
231 | | */ |
232 | | void bgp_attr_evpn_na_flag(struct attr *attr, |
233 | | uint8_t *router_flag, bool *proxy) |
234 | 107 | { |
235 | 107 | struct ecommunity *ecom; |
236 | 107 | uint32_t i; |
237 | 107 | uint8_t val; |
238 | | |
239 | 107 | ecom = bgp_attr_get_ecommunity(attr); |
240 | 107 | if (!ecom || !ecom->size) |
241 | 0 | return; |
242 | | |
243 | | /* If there is a evpn na extendd community set router_flag */ |
244 | 9.19k | for (i = 0; i < ecom->size; i++) { |
245 | 9.10k | uint8_t *pnt; |
246 | 9.10k | uint8_t type, sub_type; |
247 | | |
248 | 9.10k | pnt = (ecom->val + (i * ECOMMUNITY_SIZE)); |
249 | 9.10k | type = *pnt++; |
250 | 9.10k | sub_type = *pnt++; |
251 | | |
252 | 9.10k | if (type == ECOMMUNITY_ENCODE_EVPN && |
253 | 9.10k | sub_type == ECOMMUNITY_EVPN_SUBTYPE_ND) { |
254 | 17 | val = *pnt++; |
255 | | |
256 | 17 | if (val & ECOMMUNITY_EVPN_SUBTYPE_ND_ROUTER_FLAG) |
257 | 2 | *router_flag = 1; |
258 | | |
259 | 17 | if (val & ECOMMUNITY_EVPN_SUBTYPE_PROXY_FLAG) |
260 | 10 | *proxy = true; |
261 | | |
262 | 17 | break; |
263 | 17 | } |
264 | 9.10k | } |
265 | 107 | } |
266 | | |
267 | | /* dst prefix must be AF_INET or AF_INET6 prefix, to forge EVPN prefix */ |
268 | | extern int bgp_build_evpn_prefix(int evpn_type, uint32_t eth_tag, |
269 | | struct prefix *dst) |
270 | 0 | { |
271 | 0 | struct evpn_addr *p_evpn_p; |
272 | 0 | struct prefix p2; |
273 | 0 | struct prefix *src = &p2; |
274 | |
|
275 | 0 | if (!dst || dst->family == 0) |
276 | 0 | return -1; |
277 | | /* store initial prefix in src */ |
278 | 0 | prefix_copy(src, dst); |
279 | 0 | memset(dst, 0, sizeof(struct prefix)); |
280 | 0 | p_evpn_p = &(dst->u.prefix_evpn); |
281 | 0 | dst->family = AF_EVPN; |
282 | 0 | p_evpn_p->route_type = evpn_type; |
283 | 0 | if (evpn_type == BGP_EVPN_IP_PREFIX_ROUTE) { |
284 | 0 | p_evpn_p->prefix_addr.eth_tag = eth_tag; |
285 | 0 | p_evpn_p->prefix_addr.ip_prefix_length = p2.prefixlen; |
286 | 0 | if (src->family == AF_INET) { |
287 | 0 | SET_IPADDR_V4(&p_evpn_p->prefix_addr.ip); |
288 | 0 | memcpy(&p_evpn_p->prefix_addr.ip.ipaddr_v4, |
289 | 0 | &src->u.prefix4, |
290 | 0 | sizeof(struct in_addr)); |
291 | 0 | dst->prefixlen = (uint16_t)PREFIX_LEN_ROUTE_TYPE_5_IPV4; |
292 | 0 | } else { |
293 | 0 | SET_IPADDR_V6(&p_evpn_p->prefix_addr.ip); |
294 | 0 | memcpy(&p_evpn_p->prefix_addr.ip.ipaddr_v6, |
295 | 0 | &src->u.prefix6, |
296 | 0 | sizeof(struct in6_addr)); |
297 | 0 | dst->prefixlen = (uint16_t)PREFIX_LEN_ROUTE_TYPE_5_IPV6; |
298 | 0 | } |
299 | 0 | } else |
300 | 0 | return -1; |
301 | 0 | return 0; |
302 | 0 | } |