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