]>
Commit | Line | Data |
---|---|---|
acddc0ed | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
ca776988 | 2 | /* |
3 | * | |
4 | * Copyright (C) 2000 Robert Olsson. | |
5 | * Swedish University of Agricultural Sciences | |
ca776988 | 6 | */ |
7 | ||
d62a17ae | 8 | /* |
ca776988 | 9 | * This work includes work with the following copywrite: |
10 | * | |
11 | * Copyright (C) 1997, 2000 Kunihiro Ishiguro | |
12 | * | |
13 | */ | |
14 | ||
d62a17ae | 15 | /* |
43e52561 | 16 | * Thanks to Jens Laas at Swedish University of Agricultural Sciences |
ca776988 | 17 | * for reviewing and tests. |
18 | */ | |
19 | ||
20 | ||
21 | #include <zebra.h> | |
22 | ||
ca776988 | 23 | #include "if.h" |
24 | #include "vty.h" | |
25 | #include "sockunion.h" | |
c9e52be3 | 26 | #include "sockopt.h" |
ca776988 | 27 | #include "prefix.h" |
28 | #include "command.h" | |
29 | #include "memory.h" | |
30 | #include "stream.h" | |
31 | #include "ioctl.h" | |
32 | #include "connected.h" | |
33 | #include "log.h" | |
34 | #include "zclient.h" | |
24a58196 | 35 | #include "frrevent.h" |
25dac855 | 36 | #include "privs.h" |
ead4ee99 | 37 | #include "libfrr.h" |
43e52561 | 38 | #include "lib_errors.h" |
09781197 | 39 | #include "lib/version.h" |
ca776988 | 40 | #include "zebra/interface.h" |
41 | #include "zebra/rtadv.h" | |
42 | #include "zebra/rib.h" | |
3801e764 | 43 | #include "zebra/zebra_router.h" |
ca776988 | 44 | #include "zebra/redistribute.h" |
45 | #include "zebra/irdp.h" | |
364fed6b | 46 | #include "zebra/zebra_errors.h" |
ca776988 | 47 | #include <netinet/ip_icmp.h> |
48 | ||
ab0f6155 | 49 | #include "checksum.h" |
ca776988 | 50 | #include "if.h" |
51 | #include "sockunion.h" | |
52 | #include "log.h" | |
5920b3eb | 53 | #include "network.h" |
ca776988 | 54 | |
55 | /* GLOBAL VARS */ | |
56 | ||
25dac855 | 57 | extern struct zebra_privs_t zserv_privs; |
58 | ||
e6685141 | 59 | struct event *t_irdp_raw; |
ca776988 | 60 | |
61 | /* Timer interval of irdp. */ | |
62 | int irdp_timer_interval = IRDP_DEFAULT_INTERVAL; | |
63 | ||
d62a17ae | 64 | int irdp_sock_init(void) |
ca776988 | 65 | { |
d62a17ae | 66 | int ret, i; |
67 | int save_errno; | |
68 | int sock; | |
69 | ||
0cf6db21 | 70 | frr_with_privs(&zserv_privs) { |
d62a17ae | 71 | |
01b9e3fd DL |
72 | sock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); |
73 | save_errno = errno; | |
d62a17ae | 74 | |
01b9e3fd | 75 | } |
d62a17ae | 76 | |
77 | if (sock < 0) { | |
1c50c1c0 | 78 | flog_err_sys(EC_LIB_SOCKET, "IRDP: can't create irdp socket %s", |
9df414fe | 79 | safe_strerror(save_errno)); |
d62a17ae | 80 | return sock; |
81 | }; | |
82 | ||
83 | i = 1; | |
84 | ret = setsockopt(sock, IPPROTO_IP, IP_TTL, (void *)&i, sizeof(i)); | |
85 | if (ret < 0) { | |
450971aa | 86 | flog_err_sys(EC_LIB_SOCKET, "IRDP: can't do irdp sockopt %s", |
9df414fe | 87 | safe_strerror(errno)); |
d62a17ae | 88 | close(sock); |
89 | return ret; | |
90 | }; | |
91 | ||
92 | ret = setsockopt_ifindex(AF_INET, sock, 1); | |
93 | if (ret < 0) { | |
450971aa | 94 | flog_err_sys(EC_LIB_SOCKET, "IRDP: can't do irdp sockopt %s", |
9df414fe | 95 | safe_strerror(errno)); |
d62a17ae | 96 | close(sock); |
97 | return ret; | |
98 | }; | |
99 | ||
907a2395 | 100 | event_add_read(zrouter.master, irdp_read_raw, NULL, sock, &t_irdp_raw); |
d62a17ae | 101 | |
102 | return sock; | |
ca776988 | 103 | } |
104 | ||
105 | ||
d62a17ae | 106 | static int get_pref(struct irdp_interface *irdp, struct prefix *p) |
ca776988 | 107 | { |
d62a17ae | 108 | struct listnode *node; |
109 | struct Adv *adv; | |
110 | ||
111 | /* Use default preference or use the override pref */ | |
112 | ||
113 | if (irdp->AdvPrefList == NULL) | |
114 | return irdp->Preference; | |
115 | ||
116 | for (ALL_LIST_ELEMENTS_RO(irdp->AdvPrefList, node, adv)) | |
117 | if (p->u.prefix4.s_addr == adv->ip.s_addr) | |
118 | return adv->pref; | |
119 | ||
120 | return irdp->Preference; | |
ca776988 | 121 | } |
122 | ||
123 | /* Make ICMP Router Advertisement Message. */ | |
d62a17ae | 124 | static int make_advertisement_packet(struct interface *ifp, struct prefix *p, |
125 | struct stream *s) | |
ca776988 | 126 | { |
d62a17ae | 127 | struct zebra_if *zi = ifp->info; |
ead4ee99 | 128 | struct irdp_interface *irdp = zi->irdp; |
d62a17ae | 129 | int size; |
130 | int pref; | |
d7c0a89a | 131 | uint16_t checksum; |
d62a17ae | 132 | |
133 | pref = get_pref(irdp, p); | |
134 | ||
135 | stream_putc(s, ICMP_ROUTERADVERT); /* Type. */ | |
136 | stream_putc(s, 0); /* Code. */ | |
137 | stream_putw(s, 0); /* Checksum. */ | |
138 | stream_putc(s, 1); /* Num address. */ | |
139 | stream_putc(s, 2); /* Address Entry Size. */ | |
140 | ||
141 | if (irdp->flags & IF_SHUTDOWN) | |
142 | stream_putw(s, 0); | |
143 | else | |
144 | stream_putw(s, irdp->Lifetime); | |
145 | ||
146 | stream_putl(s, htonl(p->u.prefix4.s_addr)); /* Router address. */ | |
147 | stream_putl(s, pref); | |
148 | ||
149 | /* in_cksum return network byte order value */ | |
150 | size = 16; | |
151 | checksum = in_cksum(s->data, size); | |
152 | stream_putw_at(s, 2, htons(checksum)); | |
153 | ||
154 | return size; | |
ca776988 | 155 | } |
156 | ||
d62a17ae | 157 | static void irdp_send(struct interface *ifp, struct prefix *p, struct stream *s) |
ca776988 | 158 | { |
d62a17ae | 159 | struct zebra_if *zi = ifp->info; |
ead4ee99 | 160 | struct irdp_interface *irdp = zi->irdp; |
d7c0a89a QY |
161 | uint32_t dst; |
162 | uint32_t ttl = 1; | |
d62a17ae | 163 | |
ead4ee99 DL |
164 | if (!irdp) |
165 | return; | |
d62a17ae | 166 | if (!(ifp->flags & IFF_UP)) |
167 | return; | |
168 | ||
169 | if (irdp->flags & IF_BROADCAST) | |
170 | dst = INADDR_BROADCAST; | |
171 | else | |
172 | dst = htonl(INADDR_ALLHOSTS_GROUP); | |
173 | ||
174 | if (irdp->flags & IF_DEBUG_MESSAGES) | |
2dbe669b DA |
175 | zlog_debug( |
176 | "IRDP: TX Advert on %s %pFX Holdtime=%d Preference=%d", | |
177 | ifp->name, p, | |
178 | irdp->flags & IF_SHUTDOWN ? 0 : irdp->Lifetime, | |
179 | get_pref(irdp, p)); | |
d62a17ae | 180 | |
181 | send_packet(ifp, s, dst, p, ttl); | |
ca776988 | 182 | } |
183 | ||
d62a17ae | 184 | static void irdp_advertisement(struct interface *ifp, struct prefix *p) |
ca776988 | 185 | { |
d62a17ae | 186 | struct stream *s; |
187 | s = stream_new(128); | |
188 | make_advertisement_packet(ifp, p, s); | |
189 | irdp_send(ifp, p, s); | |
190 | stream_free(s); | |
ca776988 | 191 | } |
192 | ||
e6685141 | 193 | void irdp_send_thread(struct event *t_advert) |
ca776988 | 194 | { |
d7c0a89a | 195 | uint32_t timer, tmp; |
e16d030c | 196 | struct interface *ifp = EVENT_ARG(t_advert); |
d62a17ae | 197 | struct zebra_if *zi = ifp->info; |
ead4ee99 | 198 | struct irdp_interface *irdp = zi->irdp; |
d62a17ae | 199 | struct prefix *p; |
200 | struct listnode *node, *nnode; | |
201 | struct connected *ifc; | |
202 | ||
ead4ee99 | 203 | if (!irdp) |
cc9f21da | 204 | return; |
ead4ee99 | 205 | |
d62a17ae | 206 | irdp->flags &= ~IF_SOLICIT; |
207 | ||
208 | if (ifp->connected) | |
209 | for (ALL_LIST_ELEMENTS(ifp->connected, node, nnode, ifc)) { | |
210 | p = ifc->address; | |
211 | ||
212 | if (p->family != AF_INET) | |
213 | continue; | |
214 | ||
215 | irdp_advertisement(ifp, p); | |
216 | irdp->irdp_sent++; | |
217 | } | |
218 | ||
219 | tmp = irdp->MaxAdvertInterval - irdp->MinAdvertInterval; | |
5920b3eb | 220 | timer = frr_weak_random() % (tmp + 1); |
d62a17ae | 221 | timer = irdp->MinAdvertInterval + timer; |
222 | ||
223 | if (irdp->irdp_sent < MAX_INITIAL_ADVERTISEMENTS | |
224 | && timer > MAX_INITIAL_ADVERT_INTERVAL) | |
225 | timer = MAX_INITIAL_ADVERT_INTERVAL; | |
226 | ||
227 | if (irdp->flags & IF_DEBUG_MISC) | |
9165c5f5 | 228 | zlog_debug("IRDP: New timer for %s set to %u", ifp->name, |
d62a17ae | 229 | timer); |
230 | ||
231 | irdp->t_advertise = NULL; | |
907a2395 DS |
232 | event_add_timer(zrouter.master, irdp_send_thread, ifp, timer, |
233 | &irdp->t_advertise); | |
ca776988 | 234 | } |
235 | ||
236 | void irdp_advert_off(struct interface *ifp) | |
237 | { | |
d62a17ae | 238 | struct zebra_if *zi = ifp->info; |
ead4ee99 | 239 | struct irdp_interface *irdp = zi->irdp; |
d62a17ae | 240 | struct listnode *node, *nnode; |
241 | int i; | |
242 | struct connected *ifc; | |
243 | struct prefix *p; | |
244 | ||
ead4ee99 DL |
245 | if (!irdp) |
246 | return; | |
247 | ||
e16d030c | 248 | EVENT_OFF(irdp->t_advertise); |
d62a17ae | 249 | |
250 | if (ifp->connected) | |
251 | for (ALL_LIST_ELEMENTS(ifp->connected, node, nnode, ifc)) { | |
252 | p = ifc->address; | |
253 | ||
254 | /* Output some packets with Lifetime 0 | |
255 | we should add a wait... | |
256 | */ | |
257 | ||
258 | for (i = 0; i < IRDP_LAST_ADVERT_MESSAGES; i++) { | |
259 | irdp->irdp_sent++; | |
260 | irdp_advertisement(ifp, p); | |
261 | } | |
262 | } | |
ca776988 | 263 | } |
264 | ||
265 | ||
d62a17ae | 266 | void process_solicit(struct interface *ifp) |
ca776988 | 267 | { |
d62a17ae | 268 | struct zebra_if *zi = ifp->info; |
ead4ee99 | 269 | struct irdp_interface *irdp = zi->irdp; |
d7c0a89a | 270 | uint32_t timer; |
ca776988 | 271 | |
ead4ee99 DL |
272 | if (!irdp) |
273 | return; | |
274 | ||
d62a17ae | 275 | /* When SOLICIT is active we reject further incoming solicits |
276 | this keeps down the answering rate so we don't have think | |
277 | about DoS attacks here. */ | |
ca776988 | 278 | |
d62a17ae | 279 | if (irdp->flags & IF_SOLICIT) |
280 | return; | |
ca776988 | 281 | |
d62a17ae | 282 | irdp->flags |= IF_SOLICIT; |
e16d030c | 283 | EVENT_OFF(irdp->t_advertise); |
ca776988 | 284 | |
5920b3eb | 285 | timer = (frr_weak_random() % MAX_RESPONSE_DELAY) + 1; |
ca776988 | 286 | |
d62a17ae | 287 | irdp->t_advertise = NULL; |
907a2395 DS |
288 | event_add_timer(zrouter.master, irdp_send_thread, ifp, timer, |
289 | &irdp->t_advertise); | |
ca776988 | 290 | } |
291 | ||
2eb27eec | 292 | static int irdp_finish(void) |
ca776988 | 293 | { |
d62a17ae | 294 | struct vrf *vrf; |
d62a17ae | 295 | struct interface *ifp; |
296 | struct zebra_if *zi; | |
297 | struct irdp_interface *irdp; | |
298 | ||
299 | zlog_info("IRDP: Received shutdown notification."); | |
300 | ||
a2addae8 | 301 | RB_FOREACH (vrf, vrf_id_head, &vrfs_by_id) |
451fda4f | 302 | FOR_ALL_INTERFACES (vrf, ifp) { |
a2addae8 RW |
303 | zi = ifp->info; |
304 | ||
305 | if (!zi) | |
306 | continue; | |
307 | irdp = zi->irdp; | |
308 | if (!irdp) | |
309 | continue; | |
310 | ||
311 | if (irdp->flags & IF_ACTIVE) { | |
312 | irdp->flags |= IF_SHUTDOWN; | |
313 | irdp_advert_off(ifp); | |
314 | } | |
d62a17ae | 315 | } |
ead4ee99 | 316 | return 0; |
ca776988 | 317 | } |
318 | ||
cd9d0537 | 319 | static int irdp_init(struct event_loop *master) |
2eb27eec DL |
320 | { |
321 | irdp_if_init(); | |
322 | ||
323 | hook_register(frr_early_fini, irdp_finish); | |
8dc1f7fc DL |
324 | return 0; |
325 | } | |
326 | ||
327 | static int irdp_module_init(void) | |
328 | { | |
329 | hook_register(frr_late_init, irdp_init); | |
330 | return 0; | |
2eb27eec DL |
331 | } |
332 | ||
996c9314 | 333 | FRR_MODULE_SETUP(.name = "zebra_irdp", .version = FRR_VERSION, |
80413c20 DL |
334 | .description = "zebra IRDP module", .init = irdp_module_init, |
335 | ); |