Line data Source code
1 : /*
2 : * IP MSDP socket management
3 : * Copyright (C) 2016 Cumulus Networks, Inc.
4 : *
5 : * This program is free software; you can redistribute it and/or modify
6 : * it under the terms of the GNU General Public License as published by
7 : * the Free Software Foundation; either version 2 of the License, or
8 : * (at your option) any later version.
9 : *
10 : * This program is distributed in the hope that it will be useful, but
11 : * WITHOUT ANY WARRANTY; without even the implied warranty of
12 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 : * General Public License for 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 <lib/log.h>
23 : #include <lib/network.h>
24 : #include <lib/sockunion.h>
25 : #include <lib/thread.h>
26 : #include <lib/vty.h>
27 : #include <lib/if.h>
28 : #include <lib/vrf.h>
29 : #include <lib/lib_errors.h>
30 :
31 : #include "pimd.h"
32 : #include "pim_instance.h"
33 : #include "pim_sock.h"
34 : #include "pim_errors.h"
35 :
36 : #include "pim_msdp.h"
37 : #include "pim_msdp_socket.h"
38 :
39 : #include "sockopt.h"
40 :
41 : /* increase socket send buffer size */
42 0 : static void pim_msdp_update_sock_send_buffer_size(int fd)
43 : {
44 0 : int size = PIM_MSDP_SOCKET_SNDBUF_SIZE;
45 0 : int optval;
46 0 : socklen_t optlen = sizeof(optval);
47 :
48 0 : if (getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &optval, &optlen) < 0) {
49 0 : flog_err_sys(EC_LIB_SOCKET,
50 : "getsockopt of SO_SNDBUF failed %s",
51 : safe_strerror(errno));
52 0 : return;
53 : }
54 :
55 0 : if (optval < size) {
56 0 : if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &size, sizeof(size))
57 : < 0) {
58 0 : flog_err_sys(EC_LIB_SOCKET,
59 : "Couldn't increase send buffer: %s",
60 : safe_strerror(errno));
61 : }
62 : }
63 : }
64 :
65 : /* passive peer socket accept */
66 0 : static void pim_msdp_sock_accept(struct thread *thread)
67 : {
68 0 : union sockunion su;
69 0 : struct pim_instance *pim = THREAD_ARG(thread);
70 0 : int accept_sock;
71 0 : int msdp_sock;
72 0 : struct pim_msdp_peer *mp;
73 :
74 0 : sockunion_init(&su);
75 :
76 : /* re-register accept thread */
77 0 : accept_sock = THREAD_FD(thread);
78 0 : if (accept_sock < 0) {
79 0 : flog_err(EC_LIB_DEVELOPMENT, "accept_sock is negative value %d",
80 : accept_sock);
81 0 : return;
82 : }
83 0 : pim->msdp.listener.thread = NULL;
84 0 : thread_add_read(router->master, pim_msdp_sock_accept, pim, accept_sock,
85 : &pim->msdp.listener.thread);
86 :
87 : /* accept client connection. */
88 0 : msdp_sock = sockunion_accept(accept_sock, &su);
89 0 : if (msdp_sock < 0) {
90 0 : flog_err_sys(EC_LIB_SOCKET, "pim_msdp_sock_accept failed (%s)",
91 : safe_strerror(errno));
92 0 : return;
93 : }
94 :
95 : /* see if have peer config for this */
96 0 : mp = pim_msdp_peer_find(pim, su.sin.sin_addr);
97 0 : if (!mp || !PIM_MSDP_PEER_IS_LISTENER(mp)) {
98 0 : ++pim->msdp.rejected_accepts;
99 0 : if (PIM_DEBUG_MSDP_EVENTS) {
100 0 : flog_err(EC_PIM_MSDP_PACKET,
101 : "msdp peer connection refused from %pSU", &su);
102 : }
103 0 : close(msdp_sock);
104 0 : return;
105 : }
106 :
107 0 : if (PIM_DEBUG_MSDP_INTERNAL) {
108 0 : zlog_debug("MSDP peer %s accept success%s", mp->key_str,
109 : mp->fd >= 0 ? "(dup)" : "");
110 : }
111 :
112 : /* if we have an existing connection we need to kill that one
113 : * with this one */
114 0 : if (mp->fd >= 0) {
115 0 : if (PIM_DEBUG_MSDP_EVENTS) {
116 0 : zlog_notice(
117 : "msdp peer new connection from %pSU stop old connection",
118 : &su);
119 : }
120 0 : pim_msdp_peer_stop_tcp_conn(mp, true /* chg_state */);
121 : }
122 0 : mp->fd = msdp_sock;
123 0 : set_nonblocking(mp->fd);
124 0 : pim_msdp_update_sock_send_buffer_size(mp->fd);
125 0 : pim_msdp_peer_established(mp);
126 : }
127 :
128 : /* global listener for the MSDP well know TCP port */
129 0 : int pim_msdp_sock_listen(struct pim_instance *pim)
130 : {
131 0 : int sock;
132 0 : int socklen;
133 0 : struct sockaddr_in sin;
134 0 : int rc;
135 0 : struct pim_msdp_listener *listener = &pim->msdp.listener;
136 :
137 0 : if (pim->msdp.flags & PIM_MSDPF_LISTENER) {
138 : /* listener already setup */
139 : return 0;
140 : }
141 :
142 0 : sock = socket(AF_INET, SOCK_STREAM, 0);
143 0 : if (sock < 0) {
144 0 : flog_err_sys(EC_LIB_SOCKET, "socket: %s", safe_strerror(errno));
145 0 : return sock;
146 : }
147 :
148 0 : memset(&sin, 0, sizeof(struct sockaddr_in));
149 0 : sin.sin_family = AF_INET;
150 0 : sin.sin_port = htons(PIM_MSDP_TCP_PORT);
151 0 : socklen = sizeof(struct sockaddr_in);
152 : #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
153 : sin.sin_len = socklen;
154 : #endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */
155 :
156 0 : sockopt_reuseaddr(sock);
157 0 : sockopt_reuseport(sock);
158 :
159 0 : if (pim->vrf->vrf_id != VRF_DEFAULT) {
160 0 : struct interface *ifp =
161 0 : if_lookup_by_name(pim->vrf->name, pim->vrf->vrf_id);
162 0 : if (!ifp) {
163 0 : flog_err(EC_LIB_INTERFACE,
164 : "%s: Unable to lookup vrf interface: %s",
165 : __func__, pim->vrf->name);
166 0 : close(sock);
167 0 : return -1;
168 : }
169 0 : if (pim_socket_bind(sock, ifp)) {
170 0 : flog_err_sys(EC_LIB_SOCKET,
171 : "%s: Unable to bind to socket: %s",
172 : __func__, safe_strerror(errno));
173 0 : close(sock);
174 0 : return -1;
175 : }
176 : }
177 :
178 0 : frr_with_privs(&pimd_privs) {
179 : /* bind to well known TCP port */
180 0 : rc = bind(sock, (struct sockaddr *)&sin, socklen);
181 : }
182 :
183 0 : if (rc < 0) {
184 0 : flog_err_sys(EC_LIB_SOCKET,
185 : "pim_msdp_socket bind to port %d: %s",
186 : ntohs(sin.sin_port), safe_strerror(errno));
187 0 : close(sock);
188 0 : return rc;
189 : }
190 :
191 0 : rc = listen(sock, 3 /* backlog */);
192 0 : if (rc < 0) {
193 0 : flog_err_sys(EC_LIB_SOCKET, "pim_msdp_socket listen: %s",
194 : safe_strerror(errno));
195 0 : close(sock);
196 0 : return rc;
197 : }
198 :
199 : /* Set socket DSCP byte */
200 0 : if (setsockopt_ipv4_tos(sock, IPTOS_PREC_INTERNETCONTROL)) {
201 0 : zlog_warn("can't set sockopt IP_TOS to MSDP socket %d: %s",
202 : sock, safe_strerror(errno));
203 : }
204 :
205 : /* add accept thread */
206 0 : listener->fd = sock;
207 0 : memcpy(&listener->su, &sin, socklen);
208 0 : thread_add_read(pim->msdp.master, pim_msdp_sock_accept, pim, sock,
209 : &listener->thread);
210 :
211 0 : pim->msdp.flags |= PIM_MSDPF_LISTENER;
212 0 : return 0;
213 : }
214 :
215 : /* active peer socket setup */
216 0 : int pim_msdp_sock_connect(struct pim_msdp_peer *mp)
217 : {
218 0 : int rc;
219 :
220 0 : if (PIM_DEBUG_MSDP_INTERNAL) {
221 0 : zlog_debug("MSDP peer %s attempt connect%s", mp->key_str,
222 : mp->fd < 0 ? "" : "(dup)");
223 : }
224 :
225 : /* if we have an existing connection we need to kill that one
226 : * with this one */
227 0 : if (mp->fd >= 0) {
228 0 : if (PIM_DEBUG_MSDP_EVENTS) {
229 0 : zlog_notice(
230 : "msdp duplicate connect to %s nuke old connection",
231 : mp->key_str);
232 : }
233 0 : pim_msdp_peer_stop_tcp_conn(mp, false /* chg_state */);
234 : }
235 :
236 : /* Make socket for the peer. */
237 0 : mp->fd = sockunion_socket(&mp->su_peer);
238 0 : if (mp->fd < 0) {
239 0 : flog_err_sys(EC_LIB_SOCKET,
240 : "pim_msdp_socket socket failure: %s",
241 : safe_strerror(errno));
242 0 : return -1;
243 : }
244 :
245 0 : if (mp->pim->vrf->vrf_id != VRF_DEFAULT) {
246 0 : struct interface *ifp = if_lookup_by_name(mp->pim->vrf->name,
247 : mp->pim->vrf->vrf_id);
248 0 : if (!ifp) {
249 0 : flog_err(EC_LIB_INTERFACE,
250 : "%s: Unable to lookup vrf interface: %s",
251 : __func__, mp->pim->vrf->name);
252 0 : return -1;
253 : }
254 0 : if (pim_socket_bind(mp->fd, ifp)) {
255 0 : flog_err_sys(EC_LIB_SOCKET,
256 : "%s: Unable to bind to socket: %s",
257 : __func__, safe_strerror(errno));
258 0 : close(mp->fd);
259 0 : mp->fd = -1;
260 0 : return -1;
261 : }
262 : }
263 :
264 0 : set_nonblocking(mp->fd);
265 :
266 : /* Set socket send buffer size */
267 0 : pim_msdp_update_sock_send_buffer_size(mp->fd);
268 0 : sockopt_reuseaddr(mp->fd);
269 0 : sockopt_reuseport(mp->fd);
270 :
271 : /* source bind */
272 0 : rc = sockunion_bind(mp->fd, &mp->su_local, 0, &mp->su_local);
273 0 : if (rc < 0) {
274 0 : flog_err_sys(EC_LIB_SOCKET,
275 : "pim_msdp_socket connect bind failure: %s",
276 : safe_strerror(errno));
277 0 : close(mp->fd);
278 0 : mp->fd = -1;
279 0 : return rc;
280 : }
281 :
282 : /* Set socket DSCP byte */
283 0 : if (setsockopt_ipv4_tos(mp->fd, IPTOS_PREC_INTERNETCONTROL)) {
284 0 : zlog_warn("can't set sockopt IP_TOS to MSDP socket %d: %s",
285 : mp->fd, safe_strerror(errno));
286 : }
287 :
288 : /* Connect to the remote mp. */
289 0 : return (sockunion_connect(mp->fd, &mp->su_peer,
290 0 : htons(PIM_MSDP_TCP_PORT), 0));
291 : }
|