-
Notifications
You must be signed in to change notification settings - Fork 0
/
ancp_server.c
357 lines (322 loc) · 11.1 KB
/
ancp_server.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
#define _GNU_SOURCE
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <signal.h>
#include <sys/epoll.h>
#include <fcntl.h>
#include <stdint.h>
#include <sys/time.h>
#include "ancp.h"
#include "getmac.h"
#include "bitset.h"
#include "tcp_readwr.h"
#include "ancp_fsm.h"
#include "scli_hdlr.h"
#include "logg.h"
#include "utils.h"
#include "timer.h"
#include "list.h"
#include <assert.h>
#define PORT "6068" /* ANCP listen port */
#define CLI_PORT "6090"
#define LISTEN_BACKLOG 10 /* many pending connections queue will hold */
#define MAX_EPOLL_SIZE 1000
#define MAX_CONNECTIONS 1000
#define MAXBUF 65535
#define TIMER_INTERVAL_SEC 1 /* 1 sec */
conn_t connection[MAX_CONNECTIONS]; /* Index by fd */
t_node *timer_list = NULL; /* Adj, keepalive timer list */
list_t *del_fd_list = NULL;
/* get sockaddr, IPv4 or IPv6: */
void *get_in_addr(struct sockaddr *sa)
{
if (sa->sa_family == AF_INET) {
return &(((struct sockaddr_in*)sa)->sin_addr);
}
return &(((struct sockaddr_in6*)sa)->sin6_addr);
}
/* called from the timer lib on a timer fire */
void timer_cbk (int type, int fd)
{
if (type == ADJ_TIMER) {
DEBUG_DET("TIMER: fd %d timer fire, now in [%s]\n", fd,
state_str(connection[fd].state->curr_state) );
/* use the fd */
reset_state_counters(connection[fd].state);
/* do work on the fd */
switch (connection[fd].state->curr_state) {
case ESTAB:
send_ACK(fd, NULL, connection[fd].state);
break;
case SYNSENT:
send_SYN(fd, NULL, connection[fd].state);
break;
case SYNRCVD:
send_SYNACK(fd, NULL, connection[fd].state);
break;
default:
break; /* shut up warning*/
}
} else {
assert(type == KEEPL_TIMER);
DEBUG_ERR("TIMER: fd %d dead timer fired, cleared!\n", fd);
/*
* add FDs to be deleted in list which shall
* be deleted in the main epoll_wait loop
*/
printf ("Adding %d to del list\n", fd);
ins_list(&del_fd_list, fd);
}
}
/*
* we will be called if there was a ACK (the keepalive mechanism of ANCP)
* on FD - we'll upd keepalive timer
*/
void handle_keepalive (int fd, conn_t *conn)
{
t_node *tnode;
ancp_state_t *state = conn->state;
/* Insert a keepalive node into the timer list if this
is the first message */
// printf ("Received keepalive on fd %d\n", fd );
if (conn->dead_timer) {
/* If incoming message is a SYN, update the keepalive timer */
/* we need to remove the node from connection and re insert */
// printf("b4 keepl nod rm %d[%d], list:", conn->dead_timer->interval, conn->dead_timer->data); print_list(timer_list);
rm_tnode(&timer_list, conn->dead_timer);
// printf("aftr remove list:"); print_list(timer_list);
// printf("\n");
}
tnode = KEEPL_NODE(state->peer_timer * 3, fd);
insert_tnode(&timer_list, tnode);
conn->dead_timer = tnode;
// printf("aftr nod %d[%d] add list:", tnode->time, tnode->data); print_list(timer_list);
//printf("syn af keepl insert %d[%d]: ", state->peer_timer * 3, fd); print_list(timer_list);
DEBUG_DET("TIMER: Keepalive updated on %d\n", fd);
}
/* timer added a new node for this fd, update the pointers */
void timer_new_node_add (int type, int data, t_node *node)
{
if (type == ADJ_TIMER)
connection[data].timer = node;
else
connection[data].dead_timer = node;
}
int handle_io (int fd, uint32_t events)
{
int len = 0, n;
unsigned char buf[MAXBUF + 1] = {0};
unsigned char *p;
uint16_t type;
if (events & EPOLLIN) {
len = recv(fd, buf, 4, MSG_PEEK); /* read the header only */
if (len <= 0 ) {
DEBUG_DET("Read 0 on %d\n", fd);
perror("fd err recv");
printf("Read ret %d on fd %d\n", len, fd);
return -1;
}
p = buf;
type = GETSHORT(p);
p += 2;
len = GETSHORT(p);
p += 2;
if (type == GSMP_TYPE) {
if ((n = readn(fd, buf, len + 4)) < 0)
DEBUG_ERR("Couldn't read %d bytes in one read\n", len + 4);
else
ancp_fsm (fd, buf, n, &connection[fd], handle_keepalive);
} else {
len = recv(fd, buf, MAXBUF, 0);
DEBUG_ERR("Not a GSMP message, discard %d\n", len);
}
}
return len;
}
void clear_fd (int epfd, int fd, struct epoll_event *ev, int *cur_nfds)
{
int res;
res = epoll_ctl(epfd, EPOLL_CTL_DEL, fd, ev);
close (fd);
/* remove fd from list */
free(connection[fd].state);
connection[fd].state = NULL;
/* rm timer node from adjacency and keepalive timer list */
rm_tnode(&timer_list, connection[fd].timer);
rm_tnode(&timer_list, connection[fd].dead_timer);
if (res < 0 ) {
perror("epoll_ctl: remove");
} else {
*cur_nfds --;
}
DEBUG_INF("Closed and deleted fd %d\n", fd);
}
void timer_handler ()
{
if (timer_list)
process_timer(&timer_list);
}
void get_service (struct addrinfo *hints, int *sock, const char *port)
{
struct addrinfo *servinfo, *p;
int rv;
int yes=1;
if ((rv = getaddrinfo(NULL, port, hints, &servinfo)) != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
exit(1);
}
/* loop through all the results and bind to the first we can */
for(p = servinfo; p != NULL; p = p->ai_next) {
if ((*sock = socket(p->ai_family, p->ai_socktype,
p->ai_protocol)) == -1) {
perror("server: socket");
continue;
}
if (setsockopt(*sock, SOL_SOCKET, SO_REUSEADDR, &yes,
sizeof(int)) == -1) {
perror("setsockopt");
exit(1);
}
if (bind(*sock, p->ai_addr, p->ai_addrlen) == -1) {
close(*sock);
perror("server: bind");
continue;
}
break;
}
if (p == NULL) {
fprintf(stderr, "server: failed to bind %s\n", port);
exit(2);
}
freeaddrinfo(servinfo); /* all done with this structure*/
}
int main (int argc, char **argv)
{
int listener, cli_fd;
struct addrinfo hints;
struct sockaddr_storage their_addr; /* connector's address information*/
socklen_t sin_size;
char s[INET6_ADDRSTRLEN];
int epfd;
static struct epoll_event ev, events[MAX_EPOLL_SIZE];
int client_sock;
int nfds;
int res, i, fd;
int cur_nfds = 0;
unsigned char our_name[MAX_NAME + 1];
t_node *tnode = NULL;
int d;
get_macip_if("bond0", our_name, NULL); /* get sender name */
init_logger(basename(argv[0]));
init_timer(TIMER_INTERVAL_SEC, timer_new_node_add);
DEBUG_INF("Using %s interface, MAC = %s\n", "bond0", our_name);
/* create and bind the TCP listener sock */
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE; /* use my IP*/
get_service (&hints, &listener, PORT);
if (listen(listener, LISTEN_BACKLOG) == -1) {
perror("listen");
exit(1);
}
printf("server: listening for connections on %s...\n", PORT);
/* create and bind the UDP CLI sock */
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_DGRAM;
hints.ai_flags = AI_PASSIVE; /* use my IP*/
get_service (&hints, &cli_fd, CLI_PORT);
printf("server: listening for cli on %s...\n", CLI_PORT);
epfd = epoll_create(MAX_EPOLL_SIZE);
if (epfd < 0) {
perror("epoll_create");
exit(1);
}
cur_nfds = 0;
ev.events = EPOLLIN | EPOLLERR | EPOLLHUP;
ev.data.fd = listener;
res = epoll_ctl(epfd, EPOLL_CTL_ADD, listener, &ev);
if (res < 0)
DIE_PERR("epoll_ctl listener insert err");
cur_nfds++;
ev.data.fd = cli_fd;
res = epoll_ctl(epfd, EPOLL_CTL_ADD, cli_fd, &ev);
if (res < 0)
DIE_PERR("epoll_ctl cli fd insert err");
cur_nfds++;
while (1) {
nfds = epoll_wait(epfd, events, cur_nfds, TIMER_INTERVAL_SEC * 1000);
if (nfds < 0) {
DIE_PERR("Error in epoll_wait!");
} else if (!nfds) {
timer_handler();
/* If keepalive timer added some FDs to be removed, clear them */
while (del_fd_list) {
d = rm_list (&del_fd_list);
printf ("Deletion of %d due to keepalive timer \n", d);
clear_fd (epfd, d, &ev, &cur_nfds);
}
del_fd_list = NULL; // FIXME
}
/* for each ready socket */
for(i = 0; i < nfds; i++) {
fd = events[i].data.fd;
if (fd == listener) {
sin_size = sizeof their_addr;
client_sock =
accept(listener, (struct sockaddr *)&their_addr, &sin_size);
if (client_sock < 0) {
perror("accept");
continue;
}
inet_ntop(their_addr.ss_family,
get_in_addr((struct sockaddr *)&their_addr),
s, sizeof s);
DEBUG_INF("server: got connection from %s\n", s);
ev.events = EPOLLIN | EPOLLERR | EPOLLHUP;
ev.data.fd = client_sock;
res = epoll_ctl(epfd, EPOLL_CTL_ADD, client_sock, &ev);
if (res < 0){
perror("epoll_ctl fd insert err");
exit(1);
}
cur_nfds ++;
DEBUG_INF("server: created fd %d\n", client_sock);
/* Ad fd to conn list and init state, add to timer list also */
tnode = ADJ_NODE(ADJACENCY_TIMER,client_sock);
insert_tnode(&timer_list, tnode);
connection[client_sock].timer = tnode;
connection[client_sock].state = calloc(1,sizeof(ancp_state_t));
/* ANCP state Init */
init_state(connection[client_sock].state, our_name);
/* move the fsm with a SYN */
fsm_init (client_sock, &connection[client_sock]);
} else if (fd == cli_fd) {
if (handle_cli(fd, events[i].events) < 0) {
res = epoll_ctl(epfd, EPOLL_CTL_DEL, fd, &ev);
close (fd);
if (res < 0 )
perror("epoll_ctl: remove cli");
else
cur_nfds --;
}
} else {
if (handle_io(fd, events[i].events) < 0) {
printf ("No I/O - clearing fd %d\n", fd);
clear_fd (epfd, fd, &ev, &cur_nfds);
}
}
} /* end for {nfds}*/
} /*end while(1)*/
return 0;
}