3 * Grouped session for load balancing and fail-over
10 #include <sys/socket.h>
11 #include <linux/rtnetlink.h>
23 union grp_iphash
*idx
;
24 } grp_ip_hash
[256]; // Mapping from IP address to group structures.
26 groupidt gnextgrpid
= 0;
28 // Find gruop by IP, < 1 for not found
30 // Confusingly enough, this 'ip' must be
31 // in _network_ order. This being the common
32 // case when looking it up from IP packet headers.
33 static groupidt
grp_lookup_ipmap(in_addr_t ip
)
35 uint8_t *a
= (uint8_t *) &ip
;
36 union grp_iphash
*h
= grp_ip_hash
;
38 if (!(h
= h
[*a
++].idx
)) return 0;
39 if (!(h
= h
[*a
++].idx
)) return 0;
40 if (!(h
= h
[*a
++].idx
)) return 0;
47 sessionidt sid_loaddist
[0x10000];
51 local_group
*grp_local
= NULL
; // Array of local_group structures.
54 // Take an IP address in HOST byte order and
55 // add it to the grouid by IP cache.
57 // (It's actually cached in network order)
59 static void grp_cache_ipmap(in_addr_t ip
, groupidt g
)
61 in_addr_t nip
= htonl(ip
); // MUST be in network order. I.e. MSB must in be ((char *) (&ip))[0]
62 uint8_t *a
= (uint8_t *) &nip
;
63 union grp_iphash
*h
= grp_ip_hash
;
66 for (i
= 0; i
< 3; i
++)
68 if (!(h
[a
[i
]].idx
|| (h
[a
[i
]].idx
= calloc(256, sizeof(union grp_iphash
)))))
77 LOG(4, 0, 0, "Caching Group:%d ip address %s\n", g
, fmtaddr(nip
, 0));
79 LOG(4, 0, 0, "Un-caching Group ip address %s\n", fmtaddr(nip
, 0));
82 groupidt
grp_groupbyip(in_addr_t ip
)
84 groupidt g
= grp_lookup_ipmap(ip
);
86 if (g
> 0 && g
< MAXGROUPE
)
94 // This adds it to the routing table, advertises it
95 // via BGP if enabled, and stuffs it into the
98 // 'ip' must be in _host_ order.
100 static void grp_routeset(groupidt g
, in_addr_t ip
, int prefixlen
, int add
)
110 if (!prefixlen
) prefixlen
= 32;
112 ip
&= 0xffffffff << (32 - prefixlen
);; // Force the ip to be the first one in the route.
114 memset(&req
, 0, sizeof(req
));
118 req
.nh
.nlmsg_type
= RTM_NEWROUTE
;
119 req
.nh
.nlmsg_flags
= NLM_F_REQUEST
| NLM_F_CREATE
| NLM_F_REPLACE
;
123 req
.nh
.nlmsg_type
= RTM_DELROUTE
;
124 req
.nh
.nlmsg_flags
= NLM_F_REQUEST
;
127 req
.nh
.nlmsg_len
= NLMSG_LENGTH(sizeof(req
.rt
));
129 req
.rt
.rtm_family
= AF_INET
;
130 req
.rt
.rtm_dst_len
= prefixlen
;
131 req
.rt
.rtm_table
= RT_TABLE_MAIN
;
132 req
.rt
.rtm_protocol
= 42;
133 req
.rt
.rtm_scope
= RT_SCOPE_LINK
;
134 req
.rt
.rtm_type
= RTN_UNICAST
;
136 netlink_addattr(&req
.nh
, RTA_OIF
, &tunidx
, sizeof(int));
138 netlink_addattr(&req
.nh
, RTA_DST
, &n_ip
, sizeof(n_ip
));
140 LOG(3, 0, 0, "Route (Group) %s %s/%d\n", add
? "add" : "del", fmtaddr(htonl(ip
), 0), prefixlen
);
142 if (netlink_send(&req
.nh
) < 0)
143 LOG(0, 0, 0, "grp_routeset() error in sending netlink message: %s\n", strerror(errno
));
147 bgp_add_route(htonl(ip
), prefixlen
);
149 bgp_del_route(htonl(ip
), prefixlen
);
152 // Add/Remove the IPs to the 'groupbyip' cache.
153 // Note that we add the zero address in the case of
154 // a network route. Roll on CIDR.
156 // Note that 'g == 0' implies this is the address pool.
157 // We still cache it here, because it will pre-fill
158 // the malloc'ed tree.
161 if (!add
) // Are we deleting a route?
162 g
= 0; // Caching the session as '0' is the same as uncaching.
164 for (i
= ip
; i
< ip
+(1<<(32-prefixlen
)) ; ++i
)
166 grp_cache_ipmap(i
, g
);
167 if (!g
) cache_ipmap(i
, 0);
172 // Set all route of a group
173 void grp_setgrouproute(groupidt g
, int add
)
176 for (i
= 0; i
< grpsession
[g
].nbroutesgrp
; i
++)
178 if (grpsession
[g
].route
[i
].ip
!= 0)
180 grp_routeset(g
, grpsession
[g
].route
[i
].ip
, grpsession
[g
].route
[i
].prefixlen
, add
);
185 // return group id by session
186 groupidt
grp_groupbysession(sessionidt s
)
190 for (g
= gnextgrpid
; g
!= 0; g
= grpsession
[g
].prev
)
192 for (i
= 0; i
< grpsession
[g
].nbsession
; i
++)
194 if (grpsession
[g
].sesslist
[i
].sid
== s
)
195 { // session found in group
204 // Remove a session to a group
206 void grp_removesession(groupidt g
, sessionidt s
)
210 if (grpsession
[g
].nbsession
<= 0)
213 for (i
= 0; i
< grpsession
[g
].nbsession
; i
++)
215 if (grpsession
[g
].sesslist
[i
].sid
== s
)
216 { // session found on group
217 --grpsession
[g
].nbsession
;
218 if (grpsession
[g
].nbsession
== 0)
220 // Group is empty, remove it
223 grp_setgrouproute(g
, 0);
227 gnextgrpid
= grpsession
[g
].prev
;
232 for (g2
= gnextgrpid
; g2
!= 0; g2
= grpsession
[g2
].prev
)
234 if (grpsession
[g2
].prev
== g
)
236 grpsession
[g2
].prev
= grpsession
[g
].prev
;
242 memset(&grpsession
[g
], 0, sizeof(grpsession
[0]));
243 grpsession
[g
].state
= GROUPEFREE
;
247 // remove the session
248 memmove(&grpsession
[g
].sesslist
[i
],
249 &grpsession
[g
].sesslist
[i
+1],
250 (grpsession
[g
].nbsession
- i
) * sizeof(grpsession
[g
].sesslist
[i
]));
253 cluster_send_groupe(g
);
259 // Add a session to a group
261 static int grp_addsession(groupidt g
, sessionidt s
, uint8_t weight
)
265 for (i
= 0; i
< grpsession
[g
].nbsession
; i
++)
267 if (grpsession
[g
].sesslist
[i
].sid
== s
)
268 { // already in group
269 if ((!grpsession
[g
].sesslist
[i
].weight
) || (weight
> 1))
270 grpsession
[g
].sesslist
[i
].weight
= weight
; // update Weight session (for load-balancing)
276 if (i
>= MAXSESSINGRP
)
278 LOG(1, s
, session
[s
].tunnel
, " Too many session for Group %d\n", g
);
282 { // add session id to group
285 // it's the first session of the group, set to next group
286 grpsession
[g
].prev
= gnextgrpid
;
288 grpsession
[g
].state
= GROUPEOPEN
;
290 grpsession
[g
].sesslist
[i
].sid
= s
;
291 grpsession
[g
].sesslist
[i
].weight
= weight
;
292 grpsession
[g
].nbsession
++;
299 // Add a route to a group
301 static int grp_addroute(groupidt g
, sessionidt s
, in_addr_t ip
, int prefixlen
)
305 for (i
= 0; i
< MAXROUTEINGRP
; i
++)
307 if ((i
>= grpsession
[g
].nbroutesgrp
))
309 LOG(3, s
, session
[s
].tunnel
, " Radius reply Group %d contains route for %s/%d\n",
310 g
, fmtaddr(htonl(ip
), 0), prefixlen
);
312 grpsession
[g
].route
[i
].ip
= ip
;
313 grpsession
[g
].route
[i
].prefixlen
= prefixlen
;
314 grpsession
[g
].nbroutesgrp
++;
317 else if ((grpsession
[g
].route
[i
].ip
== ip
) && (grpsession
[g
].route
[i
].prefixlen
== prefixlen
))
319 // route already defined in group
320 LOG(3, s
, session
[s
].tunnel
,
321 " Radius reply Group %d contains route for %s/%d (this already defined)\n",
322 g
, fmtaddr(htonl(ip
), 0), prefixlen
);
326 else if (grpsession
[g
].route
[i
].ip
== 0)
328 LOG(3, s
, session
[s
].tunnel
, " Radius reply Group %d contains route for %s/%d (find empty on list!!!)\n",
329 g
, fmtaddr(htonl(ip
), 0), prefixlen
);
331 grpsession
[g
].route
[i
].ip
= ip
;
332 grpsession
[g
].route
[i
].prefixlen
= prefixlen
;
337 if (i
>= MAXROUTEINGRP
)
339 LOG(1, s
, session
[s
].tunnel
, " Too many routes for Group %d\n", g
);
344 // Process Sames vendor specific attribut radius
345 void grp_processvendorspecific(sessionidt s
, uint8_t *pvs
)
347 uint8_t attrib
= *pvs
;
349 uint8_t *n
= pvs
+ 2;
350 uint8_t *e
= pvs
+ pvs
[1];
352 if ((attrib
>= 22) && (attrib
<= 23))
354 while (n
< e
&& isdigit(*n
))
356 grpid
= grpid
* 10 + *n
- '0';
359 if ((grpid
== 0) || (grpid
>= MAXGROUPE
))
361 LOG(1, s
, session
[s
].tunnel
, " Group Attribute Id %d not allowed\n", grpid
);
366 LOG(1, s
, session
[s
].tunnel
, " Group Attribute Id not defined\n");
370 if (!grp_addsession(grpid
, s
, 1))
375 if (grpid
> config
->cluster_highest_groupeid
)
376 config
->cluster_highest_groupeid
= grpid
;
381 //process, Sames vendor-specific 64520
382 if (attrib
== 22) //SAMES-Group-Framed-Route
388 while (n
< e
&& (isdigit(*n
) || *n
== '.'))
396 u
= u
* 10 + *n
- '0';
403 while (n
< e
&& isdigit(*n
))
404 bits
= bits
* 10 + *n
++ - '0';
406 else if ((ip
>> 24) < 128)
408 else if ((ip
>> 24) < 192)
413 if (!grp_addroute(grpid
, s
, ip
, bits
))
416 else if (attrib
== 23) //SAMES-Group-Session-Weight
420 while (n
< e
&& isdigit(*n
))
421 weight
= weight
* 10 + *n
++ - '0';
425 LOG(1, s
, session
[s
].tunnel
, " Group-Session-Weight 0 GroupId %d not allowed\n", grpid
);
428 if (!grp_addsession(grpid
, s
, weight
))
435 LOG(3, s
, session
[s
].tunnel
, " Unknown vendor-specific: 64520, Attrib: %d\n", attrib
);
439 // Init data structures
444 // Set default value (10s)
445 config
->grp_txrate_average_time
= 10;
447 if (!(grpsession
= shared_malloc(sizeof(groupsesst
) * MAXGROUPE
)))
449 LOG(0, 0, 0, "Error doing malloc for grouped session: %s\n", strerror(errno
));
453 memset(grpsession
, 0, sizeof(grpsession
[0]) * MAXGROUPE
);
454 for (i
= 1; i
< MAXGROUPE
; i
++)
456 grpsession
[i
].state
= GROUPEUNDEF
;
459 if (!(grp_local
= shared_malloc(sizeof(local_group
) * MAXGROUPE
)))
461 LOG(0, 0, 0, "Error doing malloc for grp_local: %s\n", strerror(errno
));
464 memset(grp_local
, 0, sizeof(grp_local
[0]) * MAXGROUPE
);
468 // Update time_changed of the group
469 void grp_time_changed()
473 for (g
= gnextgrpid
; g
!= 0; g
= grpsession
[g
].prev
)
475 grpsession
[g
].time_changed
++;
479 // Uncache all IP of a session
480 static void grp_uncache_ipsession(groupidt g
, sessionidt s
)
489 for (i
= 0; i
< grpsession
[g
].nbroutesgrp
; i
++)
491 if (grpsession
[g
].route
[i
].ip
!= 0)
493 prefixlen
= grpsession
[g
].route
[i
].prefixlen
;
494 ip
= grpsession
[g
].route
[i
].ip
& (0xffffffff << (32 - prefixlen
)); // Force the ip to be the first one in the route.
496 for (j
= ip
; j
< ip
+(1<<(32-prefixlen
)) ; ++j
)
498 n_ip
= htonl(j
); // To network order
499 a
= (uint8_t *) &n_ip
;
502 if (!(h
= h
[*a
++].idx
)) continue;
503 if (!(h
= h
[*a
++].idx
)) continue;
504 if (!(h
= h
[*a
++].idx
)) continue;
509 //LOG(3, s, session[s].tunnel, "UnCaching ip address %s\n", fmtaddr(n_ip, 0));
516 // return the next session can be used on the group
517 sessionidt
grp_getnextsession(groupidt g
, in_addr_t ip
, in_addr_t ip_src
)
519 sessionidt s
= 0, s2
= 0, s3
= 0;
521 uint32_t ltime_changed
= 0, mintxrate
= 0xFFFFFFFF, maxtxrate
= 0;
527 if (grpsession
[g
].time_changed
>= config
->grp_txrate_average_time
)
529 // recalculation txrate
530 ltime_changed
= grpsession
[g
].time_changed
;
531 grpsession
[g
].time_changed
= 0;
532 for (i
= 0; i
< grpsession
[g
].nbsession
; i
++)
534 if ((s2
= grpsession
[g
].sesslist
[i
].sid
))
536 uint32_t coutgrp_delta
= 0;
538 if (session
[s2
].cout
>= grpsession
[g
].sesslist
[i
].prev_coutgrp
)
539 coutgrp_delta
= session
[s2
].cout
- grpsession
[g
].sesslist
[i
].prev_coutgrp
;
540 grpsession
[g
].sesslist
[i
].prev_coutgrp
= session
[s2
].cout
;
542 grpsession
[g
].sesslist
[i
].tx_rate
= coutgrp_delta
/ltime_changed
;
544 txrate
= grpsession
[g
].sesslist
[i
].tx_rate
/grpsession
[g
].sesslist
[i
].weight
;
545 if (txrate
< mintxrate
)
547 if ( session
[s2
].ppp
.phase
> Establish
&&
548 (time_now
- session
[s2
].last_packet
<= (config
->echo_timeout
+ 1)) )
550 grpsession
[g
].smin
= s2
;
555 if (txrate
> maxtxrate
)
557 if ( session
[s2
].ppp
.phase
> Establish
&&
558 (time_now
- session
[s2
].last_packet
<= (config
->echo_timeout
+ 1)) )
560 grpsession
[g
].smax
= s2
;
568 if ((s
= sessionbyip(ip
)))
570 uint8_t *as
= (uint8_t *) &ip_src
;
571 uint8_t *ad
= (uint8_t *) &ip
;
576 s
= grp_local
[g
].sid_loaddist
[ai
];
579 s
= grpsession
[g
].smin
;
580 grp_local
[g
].sid_loaddist
[ai
] = s
;
583 if (g
!= grp_groupbysession(s
))
585 // This session does not belong to this group
586 LOG(2, s
, session
[s
].tunnel
, "Warning, the session does not belong to group %d\n", g
);
588 grp_local
[g
].sid_loaddist
[ai
] = 0;
590 else if (s
== grpsession
[g
].smax
)
592 s
= grpsession
[g
].smin
;
593 grp_local
[g
].sid_loaddist
[ai
] = s
;
594 grpsession
[g
].smax
= 0;
596 else if ( (session
[s
].ppp
.phase
> Establish
) &&
597 (time_now
- session
[s
].last_packet
<= (config
->echo_timeout
+ 1)) )
604 grp_local
[g
].sid_loaddist
[ai
] = 0;
610 // random between 0 and nbsession-1
611 uint indexsess
= (rand() % grpsession
[g
].nbsession
);
613 if (indexsess
>= grpsession
[g
].nbsession
)
614 indexsess
= 0; //Sanity checks.
616 s2
= grpsession
[g
].sesslist
[indexsess
].sid
;
618 (session
[s2
].ppp
.phase
> Establish
) &&
619 (time_now
- session
[s2
].last_packet
<= (config
->echo_timeout
+ 1)))
625 for (i
= 0; i
< grpsession
[g
].nbsession
; i
++)
627 if ((s2
= grpsession
[g
].sesslist
[i
].sid
))
631 if ( session
[s2
].ppp
.phase
> Establish
&&
632 (time_now
- session
[s2
].last_packet
<= (config
->echo_timeout
+ 1)) )
646 cache_ipmap(ntohl(ip
), s
);
651 // load a groupe receive from master
652 int grp_cluster_load_groupe(groupidt g
, groupsesst
*new)
659 LOG(0, 0, 0, "ERROR: Received a group id > MAXGROUPE!\n");
663 if ((grpsession
[g
].nbroutesgrp
!= new->nbroutesgrp
) ||
664 (grpsession
[g
].nbsession
!= new->nbsession
))
671 // Check session list
672 for (i
= 0; i
< grpsession
[g
].nbsession
; i
++)
674 if (grpsession
[g
].sesslist
[i
].sid
!= new->sesslist
[i
].sid
)
685 for (i
= 0; i
< grpsession
[g
].nbroutesgrp
; i
++)
687 if (grpsession
[g
].route
[i
].ip
!= new->route
[i
].ip
)
699 grp_setgrouproute(g
, 0);
702 memcpy(&grpsession
[g
], new, sizeof(grpsession
[g
])); // Copy over..
708 grp_setgrouproute(g
, 1);