]>
Commit | Line | Data |
---|---|---|
718e3744 | 1 | /* BGP network related fucntions |
2 | Copyright (C) 1999 Kunihiro Ishiguro | |
3 | ||
4 | This file is part of GNU Zebra. | |
5 | ||
6 | GNU Zebra 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 | GNU Zebra 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 | |
17 | along with GNU Zebra; see the file COPYING. If not, write to the Free | |
18 | Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA | |
19 | 02111-1307, USA. */ | |
20 | ||
21 | #include <zebra.h> | |
22 | ||
23 | #include "thread.h" | |
24 | #include "sockunion.h" | |
0df7c91f | 25 | #include "sockopt.h" |
718e3744 | 26 | #include "memory.h" |
27 | #include "log.h" | |
28 | #include "if.h" | |
29 | #include "prefix.h" | |
30 | #include "command.h" | |
edd7c245 | 31 | #include "privs.h" |
0df7c91f | 32 | #include "linklist.h" |
718e3744 | 33 | |
34 | #include "bgpd/bgpd.h" | |
35 | #include "bgpd/bgp_fsm.h" | |
36 | #include "bgpd/bgp_attr.h" | |
37 | #include "bgpd/bgp_debug.h" | |
38 | #include "bgpd/bgp_network.h" | |
edd7c245 | 39 | |
40 | extern struct zebra_privs_t bgpd_privs; | |
41 | ||
0df7c91f PJ |
42 | \f |
43 | /* | |
44 | * Set MD5 key for the socket, for the given IPv4 peer address. | |
45 | * If the password is NULL or zero-length, the option will be disabled. | |
46 | */ | |
47 | static int | |
48 | bgp_md5_set_socket (int socket, union sockunion *su, const char *password) | |
49 | { | |
50 | int ret = -1; | |
51 | int en = ENOSYS; | |
52 | ||
53 | assert (socket >= 0); | |
54 | ||
55 | #if HAVE_DECL_TCP_MD5SIG | |
56 | ret = sockopt_tcp_signature (socket, su, password); | |
57 | en = errno; | |
58 | #endif /* HAVE_TCP_MD5SIG */ | |
59 | ||
60 | if (ret < 0) | |
61 | zlog (NULL, LOG_WARNING, "can't set TCP_MD5SIG option on socket %d: %s", | |
62 | socket, safe_strerror (en)); | |
63 | ||
64 | return ret; | |
65 | } | |
66 | ||
67 | /* Helper for bgp_connect */ | |
68 | static int | |
69 | bgp_md5_set_connect (int socket, union sockunion *su, const char *password) | |
70 | { | |
71 | int ret = -1; | |
72 | ||
73 | #if HAVE_DECL_TCP_MD5SIG | |
74 | if ( bgpd_privs.change (ZPRIVS_RAISE) ) | |
75 | { | |
76 | zlog_err ("%s: could not raise privs", __func__); | |
77 | return ret; | |
78 | } | |
79 | ||
80 | ret = bgp_md5_set_socket (socket, su, password); | |
81 | ||
82 | if (bgpd_privs.change (ZPRIVS_LOWER) ) | |
83 | zlog_err ("%s: could not lower privs", __func__); | |
84 | #endif /* HAVE_TCP_MD5SIG */ | |
85 | ||
86 | return ret; | |
87 | } | |
88 | ||
89 | int | |
90 | bgp_md5_set (struct peer *peer) | |
91 | { | |
92 | struct listnode *node; | |
93 | int fret = 0, ret; | |
94 | int *socket; | |
95 | ||
96 | if ( bgpd_privs.change (ZPRIVS_RAISE) ) | |
97 | { | |
98 | zlog_err ("%s: could not raise privs", __func__); | |
99 | return -1; | |
100 | } | |
101 | ||
102 | /* Just set the password on the listen socket(s). Outbound connections | |
103 | * are taken care of in bgp_connect() below. | |
104 | */ | |
105 | for (ALL_LIST_ELEMENTS_RO(bm->listen_sockets, node, socket)) | |
106 | { | |
107 | ret = bgp_md5_set_socket ((int )socket, &peer->su, peer->password); | |
108 | if (ret < 0) | |
109 | fret = ret; | |
110 | } | |
111 | if (bgpd_privs.change (ZPRIVS_LOWER) ) | |
112 | zlog_err ("%s: could not lower privs", __func__); | |
113 | ||
114 | return fret; | |
115 | } | |
718e3744 | 116 | \f |
117 | /* Accept bgp connection. */ | |
118 | static int | |
119 | bgp_accept (struct thread *thread) | |
120 | { | |
121 | int bgp_sock; | |
122 | int accept_sock; | |
123 | union sockunion su; | |
124 | struct peer *peer; | |
eb821189 | 125 | struct peer *peer1; |
718e3744 | 126 | struct bgp *bgp; |
127 | char buf[SU_ADDRSTRLEN]; | |
128 | ||
129 | /* Regiser accept thread. */ | |
130 | accept_sock = THREAD_FD (thread); | |
131 | bgp = THREAD_ARG (thread); | |
132 | ||
133 | if (accept_sock < 0) | |
134 | { | |
135 | zlog_err ("accept_sock is nevative value %d", accept_sock); | |
136 | return -1; | |
137 | } | |
138 | thread_add_read (master, bgp_accept, bgp, accept_sock); | |
139 | ||
140 | /* Accept client connection. */ | |
141 | bgp_sock = sockunion_accept (accept_sock, &su); | |
142 | if (bgp_sock < 0) | |
143 | { | |
6099b3b5 | 144 | zlog_err ("[Error] BGP socket accept failed (%s)", safe_strerror (errno)); |
718e3744 | 145 | return -1; |
146 | } | |
147 | ||
148 | if (BGP_DEBUG (events, EVENTS)) | |
478ba054 | 149 | zlog_debug ("[Event] BGP connection from host %s", inet_sutop (&su, buf)); |
718e3744 | 150 | |
151 | /* Check remote IP address */ | |
eb821189 | 152 | peer1 = peer_lookup (bgp, &su); |
153 | if (! peer1 || peer1->status == Idle) | |
718e3744 | 154 | { |
155 | if (BGP_DEBUG (events, EVENTS)) | |
156 | { | |
eb821189 | 157 | if (! peer1) |
478ba054 | 158 | zlog_debug ("[Event] BGP connection IP address %s is not configured", |
718e3744 | 159 | inet_sutop (&su, buf)); |
160 | else | |
478ba054 | 161 | zlog_debug ("[Event] BGP connection IP address %s is Idle state", |
718e3744 | 162 | inet_sutop (&su, buf)); |
163 | } | |
164 | close (bgp_sock); | |
165 | return -1; | |
166 | } | |
167 | ||
168 | /* In case of peer is EBGP, we should set TTL for this connection. */ | |
eb821189 | 169 | if (peer_sort (peer1) == BGP_PEER_EBGP) |
170 | sockopt_ttl (peer1->su.sa.sa_family, bgp_sock, peer1->ttl); | |
718e3744 | 171 | |
172 | if (! bgp) | |
eb821189 | 173 | bgp = peer1->bgp; |
174 | ||
175 | /* Make dummy peer until read Open packet. */ | |
176 | if (BGP_DEBUG (events, EVENTS)) | |
478ba054 | 177 | zlog_debug ("[Event] Make dummy peer structure until read Open packet"); |
eb821189 | 178 | |
179 | { | |
180 | char buf[SU_ADDRSTRLEN + 1]; | |
181 | ||
182 | peer = peer_create_accept (bgp); | |
183 | SET_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER); | |
184 | peer->su = su; | |
185 | peer->fd = bgp_sock; | |
186 | peer->status = Active; | |
187 | peer->local_id = peer1->local_id; | |
718e3744 | 188 | |
eb821189 | 189 | /* Make peer's address string. */ |
190 | sockunion2str (&su, buf, SU_ADDRSTRLEN); | |
e83e2080 | 191 | peer->host = XSTRDUP (MTYPE_BGP_PEER_HOST, buf); |
eb821189 | 192 | } |
718e3744 | 193 | |
194 | BGP_EVENT_ADD (peer, TCP_connection_open); | |
195 | ||
196 | return 0; | |
197 | } | |
198 | ||
199 | /* BGP socket bind. */ | |
94f2b392 | 200 | static int |
718e3744 | 201 | bgp_bind (struct peer *peer) |
202 | { | |
203 | #ifdef SO_BINDTODEVICE | |
204 | int ret; | |
205 | struct ifreq ifreq; | |
206 | ||
207 | if (! peer->ifname) | |
208 | return 0; | |
209 | ||
210 | strncpy ((char *)&ifreq.ifr_name, peer->ifname, sizeof (ifreq.ifr_name)); | |
211 | ||
98f5163c | 212 | if ( bgpd_privs.change (ZPRIVS_RAISE) ) |
213 | zlog_err ("bgp_bind: could not raise privs"); | |
214 | ||
eb821189 | 215 | ret = setsockopt (peer->fd, SOL_SOCKET, SO_BINDTODEVICE, |
718e3744 | 216 | &ifreq, sizeof (ifreq)); |
98f5163c | 217 | |
218 | if (bgpd_privs.change (ZPRIVS_LOWER) ) | |
219 | zlog_err ("bgp_bind: could not lower privs"); | |
220 | ||
718e3744 | 221 | if (ret < 0) |
222 | { | |
223 | zlog (peer->log, LOG_INFO, "bind to interface %s failed", peer->ifname); | |
224 | return ret; | |
225 | } | |
226 | #endif /* SO_BINDTODEVICE */ | |
227 | return 0; | |
228 | } | |
229 | ||
94f2b392 | 230 | static int |
718e3744 | 231 | bgp_bind_address (int sock, struct in_addr *addr) |
232 | { | |
233 | int ret; | |
234 | struct sockaddr_in local; | |
235 | ||
236 | memset (&local, 0, sizeof (struct sockaddr_in)); | |
237 | local.sin_family = AF_INET; | |
6f0e3f6e | 238 | #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN |
718e3744 | 239 | local.sin_len = sizeof(struct sockaddr_in); |
6f0e3f6e | 240 | #endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */ |
718e3744 | 241 | memcpy (&local.sin_addr, addr, sizeof (struct in_addr)); |
242 | ||
edd7c245 | 243 | if ( bgpd_privs.change (ZPRIVS_RAISE) ) |
244 | zlog_err ("bgp_bind_address: could not raise privs"); | |
245 | ||
718e3744 | 246 | ret = bind (sock, (struct sockaddr *)&local, sizeof (struct sockaddr_in)); |
247 | if (ret < 0) | |
248 | ; | |
edd7c245 | 249 | |
250 | if (bgpd_privs.change (ZPRIVS_LOWER) ) | |
251 | zlog_err ("bgp_bind_address: could not lower privs"); | |
252 | ||
718e3744 | 253 | return 0; |
254 | } | |
255 | ||
94f2b392 | 256 | static struct in_addr * |
718e3744 | 257 | bgp_update_address (struct interface *ifp) |
258 | { | |
259 | struct prefix_ipv4 *p; | |
260 | struct connected *connected; | |
52dc7ee6 | 261 | struct listnode *node; |
718e3744 | 262 | |
1eb8ef25 | 263 | for (ALL_LIST_ELEMENTS_RO (ifp->connected, node, connected)) |
718e3744 | 264 | { |
718e3744 | 265 | p = (struct prefix_ipv4 *) connected->address; |
266 | ||
267 | if (p->family == AF_INET) | |
268 | return &p->prefix; | |
269 | } | |
270 | return NULL; | |
271 | } | |
272 | ||
273 | /* Update source selection. */ | |
94f2b392 | 274 | static void |
718e3744 | 275 | bgp_update_source (struct peer *peer) |
276 | { | |
277 | struct interface *ifp; | |
278 | struct in_addr *addr; | |
279 | ||
280 | /* Source is specified with interface name. */ | |
281 | if (peer->update_if) | |
282 | { | |
283 | ifp = if_lookup_by_name (peer->update_if); | |
284 | if (! ifp) | |
285 | return; | |
286 | ||
287 | addr = bgp_update_address (ifp); | |
288 | if (! addr) | |
289 | return; | |
290 | ||
eb821189 | 291 | bgp_bind_address (peer->fd, addr); |
718e3744 | 292 | } |
293 | ||
294 | /* Source is specified with IP address. */ | |
295 | if (peer->update_source) | |
eb821189 | 296 | sockunion_bind (peer->fd, peer->update_source, 0, peer->update_source); |
718e3744 | 297 | } |
298 | ||
299 | /* BGP try to connect to the peer. */ | |
300 | int | |
301 | bgp_connect (struct peer *peer) | |
302 | { | |
303 | unsigned int ifindex = 0; | |
304 | ||
305 | /* Make socket for the peer. */ | |
eb821189 | 306 | peer->fd = sockunion_socket (&peer->su); |
307 | if (peer->fd < 0) | |
718e3744 | 308 | return -1; |
309 | ||
310 | /* If we can get socket for the peer, adjest TTL and make connection. */ | |
311 | if (peer_sort (peer) == BGP_PEER_EBGP) | |
eb821189 | 312 | sockopt_ttl (peer->su.sa.sa_family, peer->fd, peer->ttl); |
718e3744 | 313 | |
eb821189 | 314 | sockopt_reuseaddr (peer->fd); |
315 | sockopt_reuseport (peer->fd); | |
0df7c91f | 316 | |
1423c809 SH |
317 | #ifdef IPTOS_PREC_INTERNETCONTROL |
318 | if (sockunion_family (&peer->su) == AF_INET) | |
319 | setsockopt_ipv4_tos (peer->fd, IPTOS_PREC_INTERNETCONTROL); | |
320 | #endif | |
321 | ||
0df7c91f PJ |
322 | if (peer->password) |
323 | bgp_md5_set_connect (peer->fd, &peer->su, peer->password); | |
718e3744 | 324 | |
325 | /* Bind socket. */ | |
326 | bgp_bind (peer); | |
327 | ||
328 | /* Update source bind. */ | |
329 | bgp_update_source (peer); | |
330 | ||
331 | #ifdef HAVE_IPV6 | |
332 | if (peer->ifname) | |
333 | ifindex = if_nametoindex (peer->ifname); | |
334 | #endif /* HAVE_IPV6 */ | |
335 | ||
336 | if (BGP_DEBUG (events, EVENTS)) | |
478ba054 | 337 | plog_debug (peer->log, "%s [Event] Connect start to %s fd %d", |
eb821189 | 338 | peer->host, peer->host, peer->fd); |
718e3744 | 339 | |
340 | /* Connect to the remote peer. */ | |
eb821189 | 341 | return sockunion_connect (peer->fd, &peer->su, htons (peer->port), ifindex); |
718e3744 | 342 | } |
343 | ||
344 | /* After TCP connection is established. Get local address and port. */ | |
345 | void | |
346 | bgp_getsockname (struct peer *peer) | |
347 | { | |
348 | if (peer->su_local) | |
349 | { | |
22db9dec | 350 | sockunion_free (peer->su_local); |
718e3744 | 351 | peer->su_local = NULL; |
352 | } | |
353 | ||
354 | if (peer->su_remote) | |
355 | { | |
22db9dec | 356 | sockunion_free (peer->su_remote); |
718e3744 | 357 | peer->su_remote = NULL; |
358 | } | |
359 | ||
eb821189 | 360 | peer->su_local = sockunion_getsockname (peer->fd); |
361 | peer->su_remote = sockunion_getpeername (peer->fd); | |
718e3744 | 362 | |
363 | bgp_nexthop_set (peer->su_local, peer->su_remote, &peer->nexthop, peer); | |
364 | } | |
365 | ||
366 | /* IPv6 supported version of BGP server socket setup. */ | |
367 | #if defined (HAVE_IPV6) && ! defined (NRL) | |
368 | int | |
3a02d1f7 | 369 | bgp_socket (struct bgp *bgp, unsigned short port, char *address) |
718e3744 | 370 | { |
10d60ad1 | 371 | int ret, en; |
718e3744 | 372 | struct addrinfo req; |
373 | struct addrinfo *ainfo; | |
374 | struct addrinfo *ainfo_save; | |
375 | int sock = 0; | |
376 | char port_str[BUFSIZ]; | |
377 | ||
378 | memset (&req, 0, sizeof (struct addrinfo)); | |
379 | ||
380 | req.ai_flags = AI_PASSIVE; | |
381 | req.ai_family = AF_UNSPEC; | |
382 | req.ai_socktype = SOCK_STREAM; | |
90b68769 | 383 | snprintf (port_str, sizeof(port_str), "%d", port); |
718e3744 | 384 | port_str[sizeof (port_str) - 1] = '\0'; |
385 | ||
3a02d1f7 | 386 | ret = getaddrinfo (address, port_str, &req, &ainfo); |
718e3744 | 387 | if (ret != 0) |
388 | { | |
389 | zlog_err ("getaddrinfo: %s", gai_strerror (ret)); | |
390 | return -1; | |
391 | } | |
392 | ||
393 | ainfo_save = ainfo; | |
394 | ||
395 | do | |
396 | { | |
397 | if (ainfo->ai_family != AF_INET && ainfo->ai_family != AF_INET6) | |
398 | continue; | |
399 | ||
400 | sock = socket (ainfo->ai_family, ainfo->ai_socktype, ainfo->ai_protocol); | |
401 | if (sock < 0) | |
402 | { | |
6099b3b5 | 403 | zlog_err ("socket: %s", safe_strerror (errno)); |
718e3744 | 404 | continue; |
405 | } | |
406 | ||
407 | sockopt_reuseaddr (sock); | |
408 | sockopt_reuseport (sock); | |
edd7c245 | 409 | |
1423c809 SH |
410 | #ifdef IPTOS_PREC_INTERNETCONTROL |
411 | if (ainfo->ai_family == AF_INET) | |
412 | setsockopt_ipv4_tos (sock, IPTOS_PREC_INTERNETCONTROL); | |
413 | #endif | |
414 | ||
edd7c245 | 415 | if (bgpd_privs.change (ZPRIVS_RAISE) ) |
416 | zlog_err ("bgp_socket: could not raise privs"); | |
718e3744 | 417 | |
418 | ret = bind (sock, ainfo->ai_addr, ainfo->ai_addrlen); | |
10d60ad1 | 419 | en = errno; |
420 | if (bgpd_privs.change (ZPRIVS_LOWER) ) | |
421 | zlog_err ("bgp_bind_address: could not lower privs"); | |
422 | ||
718e3744 | 423 | if (ret < 0) |
424 | { | |
6099b3b5 | 425 | zlog_err ("bind: %s", safe_strerror (en)); |
10d60ad1 | 426 | close(sock); |
718e3744 | 427 | continue; |
428 | } | |
edd7c245 | 429 | |
718e3744 | 430 | ret = listen (sock, 3); |
431 | if (ret < 0) | |
432 | { | |
6099b3b5 | 433 | zlog_err ("listen: %s", safe_strerror (errno)); |
718e3744 | 434 | close (sock); |
435 | continue; | |
436 | } | |
0df7c91f PJ |
437 | |
438 | listnode_add (bm->listen_sockets, (void *)sock); | |
718e3744 | 439 | thread_add_read (master, bgp_accept, bgp, sock); |
440 | } | |
441 | while ((ainfo = ainfo->ai_next) != NULL); | |
442 | ||
443 | freeaddrinfo (ainfo_save); | |
444 | ||
445 | return sock; | |
446 | } | |
447 | #else | |
448 | /* Traditional IPv4 only version. */ | |
449 | int | |
3a02d1f7 | 450 | bgp_socket (struct bgp *bgp, unsigned short port, char *address) |
718e3744 | 451 | { |
452 | int sock; | |
453 | int socklen; | |
454 | struct sockaddr_in sin; | |
4a1a2716 | 455 | int ret, en; |
718e3744 | 456 | |
457 | sock = socket (AF_INET, SOCK_STREAM, 0); | |
458 | if (sock < 0) | |
459 | { | |
6099b3b5 | 460 | zlog_err ("socket: %s", safe_strerror (errno)); |
718e3744 | 461 | return sock; |
462 | } | |
463 | ||
464 | sockopt_reuseaddr (sock); | |
465 | sockopt_reuseport (sock); | |
466 | ||
1423c809 SH |
467 | #ifdef IPTOS_PREC_INTERNETCONTROL |
468 | setsockopt_ipv4_tos (sock, IPTOS_PREC_INTERNETCONTROL); | |
469 | #endif | |
470 | ||
718e3744 | 471 | memset (&sin, 0, sizeof (struct sockaddr_in)); |
472 | ||
473 | sin.sin_family = AF_INET; | |
474 | sin.sin_port = htons (port); | |
475 | socklen = sizeof (struct sockaddr_in); | |
3a02d1f7 | 476 | |
90b68769 | 477 | if (address && ((ret = inet_aton(address, &sin.sin_addr)) < 1)) |
3a02d1f7 | 478 | { |
90b68769 PJ |
479 | zlog_err("bgp_socket: could not parse ip address %s: %s", |
480 | address, safe_strerror (errno)); | |
3a02d1f7 PJ |
481 | return ret; |
482 | } | |
6f0e3f6e | 483 | #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN |
718e3744 | 484 | sin.sin_len = socklen; |
6f0e3f6e | 485 | #endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */ |
718e3744 | 486 | |
edd7c245 | 487 | if ( bgpd_privs.change (ZPRIVS_RAISE) ) |
488 | zlog_err ("bgp_socket: could not raise privs"); | |
489 | ||
718e3744 | 490 | ret = bind (sock, (struct sockaddr *) &sin, socklen); |
10d60ad1 | 491 | en = errno; |
492 | ||
493 | if (bgpd_privs.change (ZPRIVS_LOWER) ) | |
494 | zlog_err ("bgp_socket: could not lower privs"); | |
495 | ||
718e3744 | 496 | if (ret < 0) |
497 | { | |
6099b3b5 | 498 | zlog_err ("bind: %s", safe_strerror (en)); |
718e3744 | 499 | close (sock); |
500 | return ret; | |
501 | } | |
edd7c245 | 502 | |
718e3744 | 503 | ret = listen (sock, 3); |
504 | if (ret < 0) | |
505 | { | |
6099b3b5 | 506 | zlog_err ("listen: %s", safe_strerror (errno)); |
718e3744 | 507 | close (sock); |
508 | return ret; | |
509 | } | |
510 | ||
511 | thread_add_read (bm->master, bgp_accept, bgp, sock); | |
512 | ||
513 | return sock; | |
514 | } | |
515 | #endif /* HAVE_IPV6 && !NRL */ |