]>
Commit | Line | Data |
---|---|---|
077d336a RW |
1 | /* |
2 | * Copyright (C) 2020 NetDEF, Inc. | |
3 | * Renato Westphal | |
4 | * | |
5 | * This program is free software; you can redistribute it and/or modify it | |
6 | * under the terms of the GNU General Public License as published by the Free | |
7 | * Software Foundation; either version 2 of the License, or (at your option) | |
8 | * any later version. | |
9 | * | |
10 | * This program is distributed in the hope that it will be useful, but WITHOUT | |
11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
13 | * more details. | |
14 | * | |
15 | * You should have received a copy of the GNU General Public License along | |
16 | * with this program; see the file COPYING; if not, write to the Free Software | |
17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | |
18 | */ | |
19 | ||
20 | #include <zebra.h> | |
21 | ||
22 | #include "ldpd.h" | |
23 | #include "lde.h" | |
24 | #include "ldpe.h" | |
25 | #include "log.h" | |
26 | #include "ldp_debug.h" | |
27 | #include "rlfa.h" | |
28 | ||
29 | #include <lib/log.h> | |
30 | ||
31 | struct ldp_rlfa_node_head rlfa_node_tree; | |
32 | ||
33 | static int ldp_rlfa_client_compare(const struct ldp_rlfa_client *a, | |
34 | const struct ldp_rlfa_client *b) | |
35 | { | |
36 | if (a->igp.vrf_id < b->igp.vrf_id) | |
37 | return -1; | |
38 | if (a->igp.vrf_id > b->igp.vrf_id) | |
39 | return 1; | |
40 | ||
41 | if (a->igp.protocol < b->igp.protocol) | |
42 | return -1; | |
43 | if (a->igp.protocol > b->igp.protocol) | |
44 | return 1; | |
45 | ||
46 | if (a->igp.isis.spf.tree_id < b->igp.isis.spf.tree_id) | |
47 | return -1; | |
48 | if (a->igp.isis.spf.tree_id > b->igp.isis.spf.tree_id) | |
49 | return 1; | |
50 | ||
51 | if (a->igp.isis.spf.level < b->igp.isis.spf.level) | |
52 | return -1; | |
53 | if (a->igp.isis.spf.level > b->igp.isis.spf.level) | |
54 | return 1; | |
55 | ||
56 | return 0; | |
57 | } | |
58 | RB_GENERATE(ldp_rlfa_client_head, ldp_rlfa_client, entry, | |
59 | ldp_rlfa_client_compare) | |
60 | ||
61 | static int ldp_rlfa_node_compare(const struct ldp_rlfa_node *a, | |
62 | const struct ldp_rlfa_node *b) | |
63 | { | |
64 | if (ntohl(a->pq_address.s_addr) < ntohl(b->pq_address.s_addr)) | |
65 | return -1; | |
66 | if (ntohl(a->pq_address.s_addr) > ntohl(b->pq_address.s_addr)) | |
67 | return 1; | |
68 | ||
69 | return prefix_cmp(&a->destination, &b->destination); | |
70 | } | |
71 | RB_GENERATE(ldp_rlfa_node_head, ldp_rlfa_node, entry, ldp_rlfa_node_compare) | |
72 | ||
73 | struct ldp_rlfa_client *rlfa_client_new(struct ldp_rlfa_node *rnode, | |
74 | struct zapi_rlfa_igp *igp) | |
75 | { | |
76 | struct ldp_rlfa_client *rclient; | |
77 | ||
78 | if ((rclient = calloc(1, sizeof(*rclient))) == NULL) | |
79 | fatal(__func__); | |
80 | ||
81 | rclient->igp = *igp; | |
82 | rclient->node = rnode; | |
83 | RB_INSERT(ldp_rlfa_client_head, &rnode->clients, rclient); | |
84 | ||
85 | return rclient; | |
86 | } | |
87 | ||
88 | void rlfa_client_del(struct ldp_rlfa_client *rclient) | |
89 | { | |
90 | struct ldp_rlfa_node *rnode = rclient->node; | |
91 | ||
92 | RB_REMOVE(ldp_rlfa_client_head, &rnode->clients, rclient); | |
93 | free(rclient); | |
94 | ||
95 | /* Delete RLFA node if it's empty. */ | |
96 | if (RB_EMPTY(ldp_rlfa_client_head, &rnode->clients)) | |
97 | rlfa_node_del(rnode); | |
98 | } | |
99 | ||
100 | struct ldp_rlfa_client *rlfa_client_find(struct ldp_rlfa_node *rnode, | |
101 | struct zapi_rlfa_igp *igp) | |
102 | { | |
103 | struct ldp_rlfa_client rclient; | |
104 | ||
105 | rclient.igp = *igp; | |
106 | return RB_FIND(ldp_rlfa_client_head, &rnode->clients, &rclient); | |
107 | } | |
108 | ||
109 | struct ldp_rlfa_node *rlfa_node_new(const struct prefix *destination, | |
110 | struct in_addr pq_address) | |
111 | { | |
112 | struct ldp_rlfa_node *rnode; | |
113 | ||
114 | if ((rnode = calloc(1, sizeof(*rnode))) == NULL) | |
115 | fatal(__func__); | |
116 | ||
117 | rnode->destination = *destination; | |
118 | rnode->pq_address = pq_address; | |
119 | rnode->pq_label = MPLS_INVALID_LABEL; | |
120 | RB_INIT(ldp_rlfa_client_head, &rnode->clients); | |
121 | RB_INSERT(ldp_rlfa_node_head, &rlfa_node_tree, rnode); | |
122 | ||
123 | return rnode; | |
124 | } | |
125 | ||
126 | void rlfa_node_del(struct ldp_rlfa_node *rnode) | |
127 | { | |
128 | /* Delete RLFA clients. */ | |
129 | while (!RB_EMPTY(ldp_rlfa_client_head, &rnode->clients)) { | |
130 | struct ldp_rlfa_client *rclient; | |
131 | ||
132 | rclient = RB_ROOT(ldp_rlfa_client_head, &rnode->clients); | |
133 | rlfa_client_del(rclient); | |
134 | } | |
135 | ||
136 | RB_REMOVE(ldp_rlfa_node_head, &rlfa_node_tree, rnode); | |
137 | free(rnode); | |
138 | } | |
139 | ||
140 | struct ldp_rlfa_node *rlfa_node_find(const struct prefix *destination, | |
141 | struct in_addr pq_address) | |
142 | { | |
143 | struct ldp_rlfa_node rnode = {}; | |
144 | ||
145 | rnode.destination = *destination; | |
146 | rnode.pq_address = pq_address; | |
147 | return RB_FIND(ldp_rlfa_node_head, &rlfa_node_tree, &rnode); | |
148 | } | |
149 | ||
150 | void lde_rlfa_client_send(struct ldp_rlfa_client *rclient) | |
151 | { | |
152 | struct ldp_rlfa_node *rnode = rclient->node; | |
153 | struct zapi_rlfa_response rlfa_labels = {}; | |
154 | struct fec fec; | |
155 | struct fec_node *fn; | |
156 | struct fec_nh *fnh; | |
157 | int i = 0; | |
158 | ||
159 | /* Fill in inner label (allocated by PQ node). */ | |
160 | rlfa_labels.igp = rclient->igp; | |
161 | rlfa_labels.destination = rnode->destination; | |
162 | rlfa_labels.pq_label = rnode->pq_label; | |
163 | ||
164 | /* Fill in outer label(s) (allocated by the nexthop routers). */ | |
165 | fec.type = FEC_TYPE_IPV4; | |
166 | fec.u.ipv4.prefix = rnode->pq_address; | |
167 | fec.u.ipv4.prefixlen = IPV4_MAX_BITLEN; | |
168 | fn = (struct fec_node *)fec_find(&ft, &fec); | |
169 | if (!fn) | |
170 | return; | |
171 | LIST_FOREACH(fnh, &fn->nexthops, entry) { | |
172 | if (fnh->remote_label == NO_LABEL) | |
173 | continue; | |
174 | ||
175 | rlfa_labels.nexthops[i].family = fnh->af; | |
176 | switch (fnh->af) { | |
177 | case AF_INET: | |
178 | rlfa_labels.nexthops[i].gate.ipv4 = fnh->nexthop.v4; | |
179 | break; | |
180 | case AF_INET6: | |
181 | rlfa_labels.nexthops[i].gate.ipv6 = fnh->nexthop.v6; | |
182 | break; | |
183 | default: | |
184 | continue; | |
185 | } | |
186 | rlfa_labels.nexthops[i].label = fnh->remote_label; | |
187 | i++; | |
188 | } | |
189 | rlfa_labels.nexthop_num = i; | |
190 | ||
191 | lde_imsg_compose_parent(IMSG_RLFA_LABELS, 0, &rlfa_labels, | |
192 | sizeof(rlfa_labels)); | |
193 | } | |
194 | ||
195 | void lde_rlfa_label_update(const struct fec *fec) | |
196 | { | |
197 | struct ldp_rlfa_node *rnode; | |
198 | ||
199 | if (fec->type != FEC_TYPE_IPV4 | |
200 | || fec->u.ipv4.prefixlen != IPV4_MAX_BITLEN) | |
201 | return; | |
202 | ||
203 | /* | |
204 | * TODO: use an rb-tree lookup to restrict the iteration to the RLFAs | |
205 | * that were effectivelly affected by the label update. | |
206 | */ | |
207 | RB_FOREACH (rnode, ldp_rlfa_node_head, &rlfa_node_tree) { | |
208 | struct ldp_rlfa_client *rclient; | |
209 | ||
210 | if (!IPV4_ADDR_SAME(&rnode->pq_address, &fec->u.ipv4.prefix)) | |
211 | continue; | |
212 | ||
213 | RB_FOREACH (rclient, ldp_rlfa_client_head, &rnode->clients) | |
214 | lde_rlfa_client_send(rclient); | |
215 | } | |
216 | } | |
217 | ||
218 | void lde_rlfa_check(struct ldp_rlfa_client *rclient) | |
219 | { | |
220 | struct lde_nbr *ln; | |
221 | struct lde_map *me; | |
222 | struct fec fec; | |
223 | union ldpd_addr pq_address = {}; | |
224 | ||
225 | pq_address.v4 = rclient->node->pq_address; | |
226 | ln = lde_nbr_find_by_addr(AF_INET, &pq_address); | |
227 | if (!ln) | |
228 | return; | |
229 | ||
230 | lde_prefix2fec(&rclient->node->destination, &fec); | |
231 | me = (struct lde_map *)fec_find(&ln->recv_map, &fec); | |
232 | if (!me) | |
233 | return; | |
234 | ||
235 | rclient->node->pq_label = me->map.label; | |
236 | lde_rlfa_client_send(rclient); | |
237 | } | |
238 | ||
239 | /* | |
240 | * Check if there's any registered RLFA client for this prefix/neighbor (PQ | |
241 | * node) and notify about the updated label. | |
242 | */ | |
243 | void lde_rlfa_update_clients(struct fec *fec, struct lde_nbr *ln, | |
244 | uint32_t label) | |
245 | { | |
246 | struct prefix rlfa_dest; | |
247 | struct ldp_rlfa_node *rnode; | |
248 | ||
249 | lde_fec2prefix(fec, &rlfa_dest); | |
250 | rnode = rlfa_node_find(&rlfa_dest, ln->id); | |
251 | if (rnode) { | |
252 | struct ldp_rlfa_client *rclient; | |
253 | ||
254 | rnode->pq_label = label; | |
255 | RB_FOREACH (rclient, ldp_rlfa_client_head, &rnode->clients) | |
256 | lde_rlfa_client_send(rclient); | |
257 | } else | |
258 | lde_rlfa_label_update(fec); | |
259 | } | |
260 | ||
261 | void ldpe_rlfa_init(struct ldp_rlfa_client *rclient) | |
262 | { | |
263 | struct tnbr *tnbr; | |
264 | union ldpd_addr pq_address = {}; | |
265 | ||
266 | pq_address.v4 = rclient->node->pq_address; | |
267 | tnbr = tnbr_find(leconf, AF_INET, &pq_address); | |
268 | if (tnbr == NULL) { | |
269 | tnbr = tnbr_new(AF_INET, &pq_address); | |
270 | tnbr_update(tnbr); | |
271 | RB_INSERT(tnbr_head, &leconf->tnbr_tree, tnbr); | |
272 | } | |
273 | ||
274 | tnbr->rlfa_count++; | |
275 | } | |
276 | ||
277 | void ldpe_rlfa_exit(struct ldp_rlfa_client *rclient) | |
278 | { | |
279 | struct tnbr *tnbr; | |
280 | union ldpd_addr pq_address = {}; | |
281 | ||
282 | pq_address.v4 = rclient->node->pq_address; | |
283 | tnbr = tnbr_find(leconf, AF_INET, &pq_address); | |
284 | if (tnbr) { | |
285 | tnbr->rlfa_count--; | |
286 | tnbr_check(leconf, tnbr); | |
287 | } | |
288 | } |