Branch data Line data Source code
1 : : /*
2 : : *****************************************************************************
3 : : *
4 : : * File: udp_server.c
5 : : *
6 : : * Purpose: Collect SPA packets via a UDP server.
7 : : *
8 : : * Fwknop is developed primarily by the people listed in the file 'AUTHORS'.
9 : : * Copyright (C) 2009-2014 fwknop developers and contributors. For a full
10 : : * list of contributors, see the file 'CREDITS'.
11 : : *
12 : : * License (GNU General Public License):
13 : : *
14 : : * This program is free software; you can redistribute it and/or
15 : : * modify it under the terms of the GNU General Public License
16 : : * as published by the Free Software Foundation; either version 2
17 : : * of the License, or (at your option) any later version.
18 : : *
19 : : * This program is distributed in the hope that it will be useful,
20 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 : : * GNU General Public License for more details.
23 : : *
24 : : * You should have received a copy of the GNU General Public License
25 : : * along with this program; if not, write to the Free Software
26 : : * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
27 : : * USA
28 : : *
29 : : *****************************************************************************
30 : : */
31 : : #include "fwknopd_common.h"
32 : : #include "sig_handler.h"
33 : : #include "incoming_spa.h"
34 : : #include "log_msg.h"
35 : : #include "fw_util.h"
36 : : #include "utils.h"
37 : : #include <errno.h>
38 : :
39 : : #if HAVE_SYS_SOCKET_H
40 : : #include <sys/socket.h>
41 : : #endif
42 : : #if HAVE_ARPA_INET_H
43 : : #include <arpa/inet.h>
44 : : #endif
45 : : #if HAVE_NETDB
46 : : #include <netdb.h>
47 : : #endif
48 : :
49 : : #include <fcntl.h>
50 : : #include <sys/select.h>
51 : :
52 : : int
53 : 11 : run_udp_server(fko_srv_options_t *opts)
54 : : {
55 : : int s_sock, sfd_flags, selval, pkt_len;
56 : 11 : int is_err, s_timeout, rv=1;
57 : : fd_set sfd_set;
58 : : struct sockaddr_in saddr, caddr;
59 : : struct timeval tv;
60 : 11 : char sipbuf[MAX_IPV4_STR_LEN] = {0};
61 : 11 : char dgram_msg[MAX_SPA_PACKET_LEN+1] = {0};
62 : : unsigned short port;
63 : : socklen_t clen;
64 : :
65 : 11 : port = strtol_wrapper(opts->config[CONF_UDPSERV_PORT],
66 : : 1, MAX_PORT, NO_EXIT_UPON_ERR, &is_err);
67 [ - + ]: 11 : if(is_err != FKO_SUCCESS)
68 : : {
69 : 0 : log_msg(LOG_ERR, "[*] Invalid max UDPSERV_PORT value.");
70 : 0 : return -1;
71 : : }
72 : 11 : s_timeout = strtol_wrapper(opts->config[CONF_UDPSERV_SELECT_TIMEOUT],
73 : : 1, RCHK_MAX_UDPSERV_SELECT_TIMEOUT, NO_EXIT_UPON_ERR, &is_err);
74 [ - + ]: 11 : if(is_err != FKO_SUCCESS)
75 : : {
76 : 0 : log_msg(LOG_ERR, "[*] Invalid max UDPSERV_SELECT_TIMEOUT value.");
77 : 0 : return -1;
78 : : }
79 : :
80 : 11 : log_msg(LOG_INFO, "Kicking off UDP server to listen on port %i.", port);
81 : :
82 : : /* Now, let's make a UDP server
83 : : */
84 [ - + ]: 11 : if ((s_sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
85 : : {
86 : 0 : log_msg(LOG_ERR, "run_udp_server: socket() failed: %s",
87 : 0 : strerror(errno));
88 : 0 : return -1;
89 : : }
90 : :
91 : : /* Make our main socket non-blocking so we don't have to be stuck on
92 : : * listening for incoming datagrams.
93 : : */
94 [ - + ]: 11 : if((sfd_flags = fcntl(s_sock, F_GETFL, 0)) < 0)
95 : : {
96 : 0 : log_msg(LOG_ERR, "run_udp_server: fcntl F_GETFL error: %s",
97 : 0 : strerror(errno));
98 : 0 : close(s_sock);
99 : 0 : return -1;
100 : : }
101 : :
102 : 11 : sfd_flags |= O_NONBLOCK;
103 : :
104 [ - + ]: 11 : if(fcntl(s_sock, F_SETFL, sfd_flags) < 0)
105 : : {
106 : 0 : log_msg(LOG_ERR, "run_udp_server: fcntl F_SETFL error setting O_NONBLOCK: %s",
107 : 0 : strerror(errno));
108 : 0 : close(s_sock);
109 : 0 : return -1;
110 : : }
111 : :
112 : : /* Construct local address structure */
113 : : memset(&saddr, 0x0, sizeof(saddr));
114 : 11 : saddr.sin_family = AF_INET; /* Internet address family */
115 : 11 : saddr.sin_addr.s_addr = htonl(INADDR_ANY); /* Any incoming interface */
116 [ - + ]: 11 : saddr.sin_port = htons(port); /* Local port */
117 : :
118 : : /* Bind to the local address */
119 [ - + ]: 11 : if (bind(s_sock, (struct sockaddr *) &saddr, sizeof(saddr)) < 0)
120 : : {
121 : 0 : log_msg(LOG_ERR, "run_udp_server: bind() failed: %s",
122 : 0 : strerror(errno));
123 : 0 : close(s_sock);
124 : 0 : return -1;
125 : : }
126 : :
127 : : /* Initialize our signal handlers. You can check the return value for
128 : : * the number of signals that were *not* set. Those that were not set
129 : : * will be listed in the log/stderr output.
130 : : */
131 [ - + ]: 11 : if(set_sig_handlers() > 0)
132 : 0 : log_msg(LOG_ERR, "Errors encountered when setting signal handlers.");
133 : :
134 : 139 : FD_ZERO(&sfd_set);
135 : :
136 : : /* Now loop and receive SPA packets
137 : : */
138 : : while(1)
139 : : {
140 [ + + ]: 139 : if(sig_do_stop())
141 : : {
142 [ + - ]: 10 : if(opts->verbose)
143 : 10 : log_msg(LOG_INFO,
144 : : "udp_server: terminating signal received, will stop.");
145 : : break;
146 : : }
147 : :
148 : : /* Check for any expired firewall rules and deal with them.
149 : : */
150 [ + - ]: 129 : if(!opts->test)
151 : 129 : check_firewall_rules(opts);
152 : :
153 : : /* Initialize and setup the socket for select.
154 : : */
155 [ - + ][ # # ]: 129 : FD_SET(s_sock, &sfd_set);
156 : :
157 : : /* Set our select timeout to (500ms by default).
158 : : */
159 : 129 : tv.tv_sec = 0;
160 : 129 : tv.tv_usec = s_timeout;
161 : :
162 : 129 : selval = select(s_sock+1, &sfd_set, NULL, NULL, &tv);
163 : :
164 [ + + ]: 129 : if(selval == -1)
165 : : {
166 [ + - ]: 10 : if(errno == EINTR)
167 : : {
168 : : /* restart loop but only after we check for a terminating
169 : : * signal above in sig_do_stop()
170 : : */
171 : 10 : continue;
172 : : }
173 : : else
174 : : {
175 : 0 : log_msg(LOG_ERR, "run_udp_server: select error socket: %s",
176 : : strerror(errno));
177 : 0 : rv = -1;
178 : 0 : break;
179 : : }
180 : : }
181 : :
182 [ + + ]: 119 : if(selval == 0)
183 : 97 : continue;
184 : :
185 [ - + ][ # # ]: 22 : if(! FD_ISSET(s_sock, &sfd_set))
[ - + ]
186 : 0 : continue;
187 : :
188 : : /* If we make it here then there is a datagram to process
189 : : */
190 : 22 : clen = sizeof(caddr);
191 : :
192 : 44 : pkt_len = recvfrom(s_sock, dgram_msg, MAX_SPA_PACKET_LEN,
193 : : 0, (struct sockaddr *)&caddr, &clen);
194 : :
195 : 22 : dgram_msg[pkt_len] = 0x0;
196 : :
197 [ + - ]: 22 : if(opts->verbose)
198 : : {
199 : : memset(sipbuf, 0x0, MAX_IPV4_STR_LEN);
200 : 22 : inet_ntop(AF_INET, &(caddr.sin_addr.s_addr), sipbuf, MAX_IPV4_STR_LEN);
201 : 22 : log_msg(LOG_INFO, "udp_server: Got UDP datagram (%d bytes) from: %s",
202 : : pkt_len, sipbuf);
203 : : }
204 : :
205 : : /* Expect the data to not be too large
206 : : */
207 [ + - ]: 22 : if(pkt_len <= MAX_SPA_PACKET_LEN)
208 : : {
209 : : /* Copy the packet for SPA processing
210 : : */
211 : 22 : strlcpy((char *)opts->spa_pkt.packet_data, dgram_msg, pkt_len+1);
212 : 22 : opts->spa_pkt.packet_data_len = pkt_len;
213 : 22 : opts->spa_pkt.packet_proto = IPPROTO_UDP;
214 : 22 : opts->spa_pkt.packet_src_ip = caddr.sin_addr.s_addr;
215 : 22 : opts->spa_pkt.packet_dst_ip = saddr.sin_addr.s_addr;
216 [ - + ]: 22 : opts->spa_pkt.packet_src_port = ntohs(caddr.sin_port);
217 [ - + ]: 22 : opts->spa_pkt.packet_dst_port = ntohs(saddr.sin_port);
218 : :
219 : 22 : incoming_spa(opts);
220 : : }
221 : :
222 : : memset(dgram_msg, 0x0, sizeof(dgram_msg));
223 : :
224 : 22 : opts->packet_ctr += 1;
225 [ + - ][ + + ]: 22 : if(opts->foreground == 1 && opts->verbose > 2)
226 : 4 : log_msg(LOG_DEBUG, "run_udp_server() processed: %d packets",
227 : : opts->packet_ctr);
228 : :
229 [ + + ][ - + ]: 22 : if (opts->packet_ctr_limit && opts->packet_ctr >= opts->packet_ctr_limit)
230 : : {
231 : 1 : log_msg(LOG_WARNING,
232 : : "* Incoming packet count limit of %i reached",
233 : : opts->packet_ctr_limit
234 : : );
235 : 1 : break;
236 : : }
237 : :
238 : : } /* infinite while loop */
239 : :
240 : 11 : close(s_sock);
241 : 11 : return rv;
242 : : }
243 : :
244 : : /***EOF***/
|