Merge from Master
[l2tpns.git] / grpsess.c
1 /*
2 * Fernando ALVES 2013
3 * Grouped session for load balancing and fail-over
4 * GPL licenced
5 */
6
7 #include <errno.h>
8 #include <ctype.h>
9 #include <string.h>
10 #include <sys/socket.h>
11 #include <linux/rtnetlink.h>
12 #include <netinet/ip6.h>
13
14 #include "dhcp6.h"
15 #include "l2tpns.h"
16 #include "util.h"
17 #include "cluster.h"
18
19 #ifdef BGP
20 #include "bgp.h"
21 #endif
22
23 union grp_iphash {
24 groupidt grp;
25 union grp_iphash *idx;
26 } grp_ip_hash[256]; // Mapping from IP address to group structures.
27
28 groupidt gnextgrpid = 0;
29
30 typedef struct
31 {
32 sessionidt sid_loaddist[0x10000];
33 }
34 local_group;
35
36 local_group *grp_local = NULL; // Array of local_group structures.
37
38 // Find gruop by IP, < 1 for not found
39 //
40 // Confusingly enough, this 'ip' must be
41 // in _network_ order. This being the common
42 // case when looking it up from IP packet headers.
43 static groupidt grp_lookup_ipmap(in_addr_t ip)
44 {
45 uint8_t *a = (uint8_t *) &ip;
46 union grp_iphash *h = grp_ip_hash;
47
48 if (!(h = h[*a++].idx)) return 0;
49 if (!(h = h[*a++].idx)) return 0;
50 if (!(h = h[*a++].idx)) return 0;
51
52 return h[*a].grp;
53 }
54
55 //
56 // Take an IP address in HOST byte order and
57 // add it to the grouid by IP cache.
58 //
59 // (It's actually cached in network order)
60 //
61 static void grp_cache_ipmap(in_addr_t ip, groupidt g)
62 {
63 in_addr_t nip = htonl(ip); // MUST be in network order. I.e. MSB must in be ((char *) (&ip))[0]
64 uint8_t *a = (uint8_t *) &nip;
65 union grp_iphash *h = grp_ip_hash;
66 int i;
67
68 for (i = 0; i < 3; i++)
69 {
70 if (!(h[a[i]].idx || (h[a[i]].idx = calloc(256, sizeof(union grp_iphash)))))
71 return;
72
73 h = h[a[i]].idx;
74 }
75
76 h[a[3]].grp = g;
77
78 if (g > 0)
79 LOG(4, 0, 0, "Caching Group:%d ip address %s\n", g, fmtaddr(nip, 0));
80 else if (g == 0)
81 LOG(4, 0, 0, "Un-caching Group ip address %s\n", fmtaddr(nip, 0));
82 }
83
84 groupidt grp_groupbyip(in_addr_t ip)
85 {
86 groupidt g = grp_lookup_ipmap(ip);
87
88 if (g > 0 && g < MAXGROUPE)
89 return g;
90
91 return 0;
92 }
93
94 // Add a route
95 //
96 // This adds it to the routing table, advertises it
97 // via BGP if enabled, and stuffs it into the
98 // 'groupbyip' cache.
99 //
100 // 'ip' must be in _host_ order.
101 //
102 static void grp_routeset(groupidt g, in_addr_t ip, int prefixlen, int add)
103 {
104 struct {
105 struct nlmsghdr nh;
106 struct rtmsg rt;
107 char buf[32];
108 } req;
109 int i;
110 in_addr_t n_ip;
111
112 if (!prefixlen) prefixlen = 32;
113
114 ip &= 0xffffffff << (32 - prefixlen);; // Force the ip to be the first one in the route.
115
116 memset(&req, 0, sizeof(req));
117
118 if (add)
119 {
120 req.nh.nlmsg_type = RTM_NEWROUTE;
121 req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_REPLACE;
122 }
123 else
124 {
125 req.nh.nlmsg_type = RTM_DELROUTE;
126 req.nh.nlmsg_flags = NLM_F_REQUEST;
127 }
128
129 req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(req.rt));
130
131 req.rt.rtm_family = AF_INET;
132 req.rt.rtm_dst_len = prefixlen;
133 req.rt.rtm_table = RT_TABLE_MAIN;
134 req.rt.rtm_protocol = 42;
135 req.rt.rtm_scope = RT_SCOPE_LINK;
136 req.rt.rtm_type = RTN_UNICAST;
137
138 netlink_addattr(&req.nh, RTA_OIF, &tunidx, sizeof(int));
139 n_ip = htonl(ip);
140 netlink_addattr(&req.nh, RTA_DST, &n_ip, sizeof(n_ip));
141
142 LOG(3, 0, 0, "Route (Group) %s %s/%d\n", add ? "add" : "del", fmtaddr(htonl(ip), 0), prefixlen);
143
144 if (netlink_send(&req.nh) < 0)
145 LOG(0, 0, 0, "grp_routeset() error in sending netlink message: %s\n", strerror(errno));
146
147 #ifdef BGP
148 if (add)
149 bgp_add_route(htonl(ip), prefixlen);
150 else
151 bgp_del_route(htonl(ip), prefixlen);
152 #endif /* BGP */
153
154 // Add/Remove the IPs to the 'groupbyip' cache.
155 // Note that we add the zero address in the case of
156 // a network route. Roll on CIDR.
157
158 // Note that 'g == 0' implies this is the address pool.
159 // We still cache it here, because it will pre-fill
160 // the malloc'ed tree.
161 if (g)
162 {
163 if (!add) // Are we deleting a route?
164 g = 0; // Caching the session as '0' is the same as uncaching.
165
166 for (i = ip; i < ip+(1<<(32-prefixlen)) ; ++i)
167 {
168 grp_cache_ipmap(i, g);
169 if (!g) cache_ipmap(i, 0);
170 }
171 }
172 }
173
174 // Set all route of a group
175 void grp_setgrouproute(groupidt g, int add)
176 {
177 int i;
178 for (i = 0; i < grpsession[g].nbroutesgrp; i++)
179 {
180 if (grpsession[g].route[i].ip != 0)
181 {
182 grp_routeset(g, grpsession[g].route[i].ip, grpsession[g].route[i].prefixlen, add);
183 }
184 }
185 }
186
187 // return group id by session
188 groupidt grp_groupbysession(sessionidt s)
189 {
190 groupidt g = 0;
191 int i;
192 for (g = gnextgrpid; g != 0; g = grpsession[g].prev)
193 {
194 for (i = 0; i < grpsession[g].nbsession; i++)
195 {
196 if (grpsession[g].sesslist[i].sid == s)
197 { // session found in group
198 return g;
199 }
200 }
201 }
202
203 return 0;
204 }
205
206 // Remove a session to a group
207 // return 1 if OK
208 void grp_removesession(groupidt g, sessionidt s)
209 {
210 int i;
211
212 if (grpsession[g].nbsession <= 0)
213 return;
214
215 for (i = 0; i < grpsession[g].nbsession; i++)
216 {
217 if (grpsession[g].sesslist[i].sid == s)
218 { // session found on group
219 --grpsession[g].nbsession;
220 if (grpsession[g].nbsession == 0)
221 {
222 // Group is empty, remove it
223
224 // Del all routes
225 grp_setgrouproute(g, 0);
226
227 if (gnextgrpid == g)
228 {
229 gnextgrpid = grpsession[g].prev;
230 }
231 else
232 {
233 groupidt g2;
234 for (g2 = gnextgrpid; g2 != 0; g2 = grpsession[g2].prev)
235 {
236 if (grpsession[g2].prev == g)
237 {
238 grpsession[g2].prev = grpsession[g].prev;
239 break;
240 }
241 }
242 }
243
244 memset(&grpsession[g], 0, sizeof(grpsession[0]));
245 grpsession[g].state = GROUPEFREE;
246 }
247 else
248 {
249 // remove the session
250 memmove(&grpsession[g].sesslist[i],
251 &grpsession[g].sesslist[i+1],
252 (grpsession[g].nbsession - i) * sizeof(grpsession[g].sesslist[i]));
253 }
254
255 cluster_send_groupe(g);
256 return;
257 }
258 }
259 }
260
261 // Add a session to a group
262 // return 1 if OK
263 static int grp_addsession(groupidt g, sessionidt s, uint8_t weight)
264 {
265 int i;
266
267 for (i = 0; i < grpsession[g].nbsession; i++)
268 {
269 if (grpsession[g].sesslist[i].sid == s)
270 { // already in group
271 if ((!grpsession[g].sesslist[i].weight) || (weight > 1))
272 grpsession[g].sesslist[i].weight = weight; // update Weight session (for load-balancing)
273
274 return 1;
275 }
276 }
277
278 if (i >= MAXSESSINGRP)
279 {
280 LOG(1, s, session[s].tunnel, " Too many session for Group %d\n", g);
281 return 0;
282 }
283 else
284 { // add session id to group
285 if (i == 0)
286 {
287 // it's the first session of the group, set to next group
288 grpsession[g].prev = gnextgrpid;
289 gnextgrpid = g;
290 grpsession[g].state = GROUPEOPEN;
291 }
292 grpsession[g].sesslist[i].sid = s;
293 grpsession[g].sesslist[i].weight = weight;
294 grpsession[g].nbsession++;
295
296 return 1;
297 }
298 return 0;
299 }
300
301 // Add a route to a group
302 // return 1 if OK
303 static int grp_addroute(groupidt g, sessionidt s, in_addr_t ip, int prefixlen)
304 {
305 int i;
306
307 for (i = 0; i < MAXROUTEINGRP; i++)
308 {
309 if ((i >= grpsession[g].nbroutesgrp))
310 {
311 LOG(3, s, session[s].tunnel, " Radius reply Group %d contains route for %s/%d\n",
312 g, fmtaddr(htonl(ip), 0), prefixlen);
313
314 grpsession[g].route[i].ip = ip;
315 grpsession[g].route[i].prefixlen = prefixlen;
316 grpsession[g].nbroutesgrp++;
317 return 1;
318 }
319 else if ((grpsession[g].route[i].ip == ip) && (grpsession[g].route[i].prefixlen == prefixlen))
320 {
321 // route already defined in group
322 LOG(3, s, session[s].tunnel,
323 " Radius reply Group %d contains route for %s/%d (this already defined)\n",
324 g, fmtaddr(htonl(ip), 0), prefixlen);
325
326 return 1;
327 }
328 else if (grpsession[g].route[i].ip == 0)
329 {
330 LOG(3, s, session[s].tunnel, " Radius reply Group %d contains route for %s/%d (find empty on list!!!)\n",
331 g, fmtaddr(htonl(ip), 0), prefixlen);
332
333 grpsession[g].route[i].ip = ip;
334 grpsession[g].route[i].prefixlen = prefixlen;
335 return 1;
336 }
337 }
338
339 if (i >= MAXROUTEINGRP)
340 {
341 LOG(1, s, session[s].tunnel, " Too many routes for Group %d\n", g);
342 }
343 return 0;
344 }
345
346 // Process Sames vendor specific attribut radius
347 void grp_processvendorspecific(sessionidt s, uint8_t *pvs)
348 {
349 uint8_t attrib = *pvs;
350 groupidt grpid = 0;
351 uint8_t *n = pvs + 2;
352 uint8_t *e = pvs + pvs[1];
353
354 if ((attrib >= 22) && (attrib <= 23))
355 {
356 while (n < e && isdigit(*n))
357 {
358 grpid = grpid * 10 + *n - '0';
359 n++;
360 }
361 if ((grpid == 0) || (grpid >= MAXGROUPE))
362 {
363 LOG(1, s, session[s].tunnel, " Group Attribute Id %d not allowed\n", grpid);
364 return;
365 }
366 else if (*n != ',')
367 {
368 LOG(1, s, session[s].tunnel, " Group Attribute Id not defined\n");
369 return;
370 }
371
372 if (!grp_addsession(grpid, s, 1))
373 {
374 return;
375 }
376
377 if (grpid > config->cluster_highest_groupeid)
378 config->cluster_highest_groupeid = grpid;
379
380 n++;
381 }
382
383 //process, Sames vendor-specific 64520
384 if (attrib == 22) //SAMES-Group-Framed-Route
385 {
386 in_addr_t ip = 0;
387 uint8_t u = 0;
388 uint8_t bits = 0;
389
390 while (n < e && (isdigit(*n) || *n == '.'))
391 {
392 if (*n == '.')
393 {
394 ip = (ip << 8) + u;
395 u = 0;
396 }
397 else
398 u = u * 10 + *n - '0';
399 n++;
400 }
401 ip = (ip << 8) + u;
402 if (*n == '/')
403 {
404 n++;
405 while (n < e && isdigit(*n))
406 bits = bits * 10 + *n++ - '0';
407 }
408 else if ((ip >> 24) < 128)
409 bits = 8;
410 else if ((ip >> 24) < 192)
411 bits = 16;
412 else
413 bits = 24;
414
415 if (!grp_addroute(grpid, s, ip, bits))
416 return;
417 }
418 else if (attrib == 23) //SAMES-Group-Session-Weight
419 {
420 uint8_t weight = 0;
421
422 while (n < e && isdigit(*n))
423 weight = weight * 10 + *n++ - '0';
424
425 if (!weight)
426 {
427 LOG(1, s, session[s].tunnel, " Group-Session-Weight 0 GroupId %d not allowed\n", grpid);
428 return;
429 }
430 if (!grp_addsession(grpid, s, weight))
431 {
432 return;
433 }
434 }
435 else
436 {
437 LOG(3, s, session[s].tunnel, " Unknown vendor-specific: 64520, Attrib: %d\n", attrib);
438 }
439 }
440
441 // Init data structures
442 void grp_initdata()
443 {
444 int i;
445
446 // Set default value (10s)
447 config->grp_txrate_average_time = 10;
448
449 if (!(grpsession = shared_malloc(sizeof(groupsesst) * MAXGROUPE)))
450 {
451 LOG(0, 0, 0, "Error doing malloc for grouped session: %s\n", strerror(errno));
452 exit(1);
453 }
454
455 memset(grpsession, 0, sizeof(grpsession[0]) * MAXGROUPE);
456 for (i = 1; i < MAXGROUPE; i++)
457 {
458 grpsession[i].state = GROUPEUNDEF;
459 }
460
461 if (!(grp_local = shared_malloc(sizeof(local_group) * MAXGROUPE)))
462 {
463 LOG(0, 0, 0, "Error doing malloc for grp_local: %s\n", strerror(errno));
464 exit(1);
465 }
466 memset(grp_local, 0, sizeof(grp_local[0]) * MAXGROUPE);
467
468 }
469
470 // Update time_changed of the group
471 void grp_time_changed()
472 {
473 groupidt g;
474
475 for (g = gnextgrpid; g != 0; g = grpsession[g].prev)
476 {
477 grpsession[g].time_changed++;
478 }
479 }
480
481 // Uncache all IP of a session
482 static void grp_uncache_ipsession(groupidt g, sessionidt s)
483 {
484 int i;
485 uint8_t *a;
486 in_addr_t ip;
487 in_addr_t n_ip, j;
488 int prefixlen;
489 union iphash *h;
490
491 for (i = 0; i < grpsession[g].nbroutesgrp; i++)
492 {
493 if (grpsession[g].route[i].ip != 0)
494 {
495 prefixlen = grpsession[g].route[i].prefixlen;
496 ip = grpsession[g].route[i].ip & (0xffffffff << (32 - prefixlen)); // Force the ip to be the first one in the route.
497
498 for (j = ip; j < ip+(1<<(32-prefixlen)) ; ++j)
499 {
500 n_ip = htonl(j); // To network order
501 a = (uint8_t *) &n_ip;
502 h = ip_hash;
503
504 if (!(h = h[*a++].idx)) continue;
505 if (!(h = h[*a++].idx)) continue;
506 if (!(h = h[*a++].idx)) continue;
507
508 if (s == h[*a].sess)
509 {
510 h[*a].sess = 0;
511 //LOG(3, s, session[s].tunnel, "UnCaching ip address %s\n", fmtaddr(n_ip, 0));
512 }
513 }
514 }
515 }
516 }
517
518 uint16_t guint16_index_loadlist;
519 // return the next session can be used on the group
520 sessionidt grp_getnextsession(groupidt g, in_addr_t ip, in_addr_t ip_src)
521 {
522 sessionidt s = 0, s2 = 0, s3 = 0;
523 int i;
524 uint32_t ltime_changed = 0, mintxrate = 0xFFFFFFFF, maxtxrate = 0;
525 uint32_t txrate = 0;
526
527 if (g >= MAXGROUPE)
528 return 0;
529
530 if (grpsession[g].time_changed >= config->grp_txrate_average_time)
531 {
532 // recalculation txrate
533 ltime_changed = grpsession[g].time_changed;
534 grpsession[g].time_changed = 0;
535 for (i = 0; i < grpsession[g].nbsession; i++)
536 {
537 if ((s2 = grpsession[g].sesslist[i].sid))
538 {
539 uint32_t coutgrp_delta = 0;
540
541 if (session[s2].cout >= grpsession[g].sesslist[i].prev_coutgrp)
542 coutgrp_delta = session[s2].cout - grpsession[g].sesslist[i].prev_coutgrp;
543 grpsession[g].sesslist[i].prev_coutgrp = session[s2].cout;
544
545 txrate = (txrate + (coutgrp_delta/ltime_changed)) >> 1;
546 grpsession[g].sesslist[i].tx_rate = txrate;
547
548 txrate = grpsession[g].sesslist[i].tx_rate/grpsession[g].sesslist[i].weight;
549 if (txrate < mintxrate)
550 {
551 if ( session[s2].ppp.phase > Establish &&
552 (time_now - session[s2].last_packet <= (config->echo_timeout + 1)) )
553 {
554 grpsession[g].smin = s2;
555 mintxrate = txrate;
556 }
557 }
558
559 if (txrate > maxtxrate)
560 {
561 if ( session[s2].ppp.phase > Establish &&
562 (time_now - session[s2].last_packet <= (config->echo_timeout + 1)) )
563 {
564 grpsession[g].smax = s2;
565 maxtxrate = txrate;
566 }
567 }
568 }
569 }
570 }
571
572 if ((s = sessionbyip(ip)))
573 {
574 uint8_t *as = (uint8_t *) &ip_src;
575 uint8_t *ad = (uint8_t *) &ip;
576 uint16_t ai = ad[3];
577 ai <<= 8;
578 ai |= as[3];
579
580 s = grp_local[g].sid_loaddist[ai];
581 if (!s)
582 {
583 s = grpsession[g].smin;
584 grp_local[g].sid_loaddist[ai] = s;
585 }
586
587 if (g != grp_groupbysession(s))
588 {
589 // This session does not belong to this group
590 LOG(3, s, session[s].tunnel, "Warning, the session does not belong to group %d\n", g);
591 s = 0;
592 grp_local[g].sid_loaddist[ai] = 0;
593 }
594 else if ( (session[s].ppp.phase > Establish) &&
595 (time_now - session[s].last_packet <= (config->echo_timeout + 1)) )
596 {
597 grp_local[g].sid_loaddist[guint16_index_loadlist++] = 0;
598 return s;
599 }
600 else
601 {
602 s = 0;
603 grp_local[g].sid_loaddist[ai] = 0;
604 }
605 }
606
607 if (!s)
608 {
609 // random between 0 and nbsession-1
610 uint indexsess = (rand() % grpsession[g].nbsession);
611
612 if (indexsess >= grpsession[g].nbsession)
613 indexsess = 0; //Sanity checks.
614
615 s2 = grpsession[g].sesslist[indexsess].sid;
616 if (s2 &&
617 (session[s2].ppp.phase > Establish) &&
618 (time_now - session[s2].last_packet <= (config->echo_timeout + 1)))
619 {
620 s = s2;
621 }
622 else
623 {
624 for (i = 0; i < grpsession[g].nbsession; i++)
625 {
626 if ((s2 = grpsession[g].sesslist[i].sid))
627 {
628 s3 = s2;
629
630 if ( session[s2].ppp.phase > Establish &&
631 (time_now - session[s2].last_packet <= (config->echo_timeout + 1)) )
632 {
633 s = s2;
634 break;
635 }
636 }
637 }
638 }
639 }
640
641 if (!s)
642 s = s3;
643
644 if (s)
645 cache_ipmap(ntohl(ip), s);
646
647 return s;
648 }
649
650 // load a groupe receive from master
651 int grp_cluster_load_groupe(groupidt g, groupsesst *new)
652 {
653 int i;
654 int updategroup = 0;
655
656 if (g >= MAXGROUPE)
657 {
658 LOG(0, 0, 0, "ERROR: Received a group id > MAXGROUPE!\n");
659 return 0;
660 }
661
662 if ((grpsession[g].nbroutesgrp != new->nbroutesgrp) ||
663 (grpsession[g].nbsession != new->nbsession))
664 {
665 updategroup = 1;
666 }
667
668 if (!updategroup)
669 {
670 // Check session list
671 for (i = 0; i < grpsession[g].nbsession; i++)
672 {
673 if (grpsession[g].sesslist[i].sid != new->sesslist[i].sid)
674 {
675 updategroup = 1;
676 break;
677 }
678 }
679 }
680
681 if (!updategroup)
682 {
683 // Check routes list
684 for (i = 0; i < grpsession[g].nbroutesgrp; i++)
685 {
686 if (grpsession[g].route[i].ip != new->route[i].ip)
687 {
688 updategroup = 1;
689 break;
690 }
691 }
692 }
693
694 // needs update
695 if (updategroup)
696 {
697 // Del all routes
698 grp_setgrouproute(g, 0);
699 }
700
701 memcpy(&grpsession[g], new, sizeof(grpsession[g])); // Copy over..
702
703 // needs update
704 if (updategroup)
705 {
706 // Add all routes
707 grp_setgrouproute(g, 1);
708 }
709
710 return 1;
711 }