3 * Grouped session for load balancing and fail-over
10 #include <sys/socket.h>
11 #include <linux/rtnetlink.h>
12 #include <netinet/ip6.h>
25 union grp_iphash
*idx
;
26 } grp_ip_hash
[256]; // Mapping from IP address to group structures.
28 groupidt gnextgrpid
= 0;
32 sessionidt sid_loaddist
[0x10000];
36 local_group
*grp_local
= NULL
; // Array of local_group structures.
38 // Find gruop by IP, < 1 for not found
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
)
45 uint8_t *a
= (uint8_t *) &ip
;
46 union grp_iphash
*h
= grp_ip_hash
;
48 if (!(h
= h
[*a
++].idx
)) return 0;
49 if (!(h
= h
[*a
++].idx
)) return 0;
50 if (!(h
= h
[*a
++].idx
)) return 0;
56 // Take an IP address in HOST byte order and
57 // add it to the grouid by IP cache.
59 // (It's actually cached in network order)
61 static void grp_cache_ipmap(in_addr_t ip
, groupidt g
)
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
;
68 for (i
= 0; i
< 3; i
++)
70 if (!(h
[a
[i
]].idx
|| (h
[a
[i
]].idx
= calloc(256, sizeof(union grp_iphash
)))))
79 LOG(4, 0, 0, "Caching Group:%d ip address %s\n", g
, fmtaddr(nip
, 0));
81 LOG(4, 0, 0, "Un-caching Group ip address %s\n", fmtaddr(nip
, 0));
84 groupidt
grp_groupbyip(in_addr_t ip
)
86 groupidt g
= grp_lookup_ipmap(ip
);
88 if (g
> 0 && g
< MAXGROUPE
)
96 // This adds it to the routing table, advertises it
97 // via BGP if enabled, and stuffs it into the
100 // 'ip' must be in _host_ order.
102 static void grp_routeset(groupidt g
, in_addr_t ip
, int prefixlen
, int add
)
112 if (!prefixlen
) prefixlen
= 32;
114 ip
&= 0xffffffff << (32 - prefixlen
);; // Force the ip to be the first one in the route.
116 memset(&req
, 0, sizeof(req
));
120 req
.nh
.nlmsg_type
= RTM_NEWROUTE
;
121 req
.nh
.nlmsg_flags
= NLM_F_REQUEST
| NLM_F_CREATE
| NLM_F_REPLACE
;
125 req
.nh
.nlmsg_type
= RTM_DELROUTE
;
126 req
.nh
.nlmsg_flags
= NLM_F_REQUEST
;
129 req
.nh
.nlmsg_len
= NLMSG_LENGTH(sizeof(req
.rt
));
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
;
138 netlink_addattr(&req
.nh
, RTA_OIF
, &tunidx
, sizeof(int));
140 netlink_addattr(&req
.nh
, RTA_DST
, &n_ip
, sizeof(n_ip
));
142 LOG(3, 0, 0, "Route (Group) %s %s/%d\n", add
? "add" : "del", fmtaddr(htonl(ip
), 0), prefixlen
);
144 if (netlink_send(&req
.nh
) < 0)
145 LOG(0, 0, 0, "grp_routeset() error in sending netlink message: %s\n", strerror(errno
));
149 bgp_add_route(htonl(ip
), prefixlen
);
151 bgp_del_route(htonl(ip
), prefixlen
);
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.
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.
163 if (!add
) // Are we deleting a route?
164 g
= 0; // Caching the session as '0' is the same as uncaching.
166 for (i
= ip
; i
< ip
+(1<<(32-prefixlen
)) ; ++i
)
168 grp_cache_ipmap(i
, g
);
169 if (!g
) cache_ipmap(i
, 0);
174 // Set all route of a group
175 void grp_setgrouproute(groupidt g
, int add
)
178 for (i
= 0; i
< grpsession
[g
].nbroutesgrp
; i
++)
180 if (grpsession
[g
].route
[i
].ip
!= 0)
182 grp_routeset(g
, grpsession
[g
].route
[i
].ip
, grpsession
[g
].route
[i
].prefixlen
, add
);
187 // return group id by session
188 groupidt
grp_groupbysession(sessionidt s
)
192 for (g
= gnextgrpid
; g
!= 0; g
= grpsession
[g
].prev
)
194 for (i
= 0; i
< grpsession
[g
].nbsession
; i
++)
196 if (grpsession
[g
].sesslist
[i
].sid
== s
)
197 { // session found in group
206 // Remove a session to a group
208 void grp_removesession(groupidt g
, sessionidt s
)
212 if (grpsession
[g
].nbsession
<= 0)
215 for (i
= 0; i
< grpsession
[g
].nbsession
; i
++)
217 if (grpsession
[g
].sesslist
[i
].sid
== s
)
218 { // session found on group
219 --grpsession
[g
].nbsession
;
220 if (grpsession
[g
].nbsession
== 0)
222 // Group is empty, remove it
225 grp_setgrouproute(g
, 0);
229 gnextgrpid
= grpsession
[g
].prev
;
234 for (g2
= gnextgrpid
; g2
!= 0; g2
= grpsession
[g2
].prev
)
236 if (grpsession
[g2
].prev
== g
)
238 grpsession
[g2
].prev
= grpsession
[g
].prev
;
244 memset(&grpsession
[g
], 0, sizeof(grpsession
[0]));
245 grpsession
[g
].state
= GROUPEFREE
;
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
]));
255 cluster_send_groupe(g
);
261 // Add a session to a group
263 static int grp_addsession(groupidt g
, sessionidt s
, uint8_t weight
)
267 for (i
= 0; i
< grpsession
[g
].nbsession
; i
++)
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)
278 if (i
>= MAXSESSINGRP
)
280 LOG(1, s
, session
[s
].tunnel
, " Too many session for Group %d\n", g
);
284 { // add session id to group
287 // it's the first session of the group, set to next group
288 grpsession
[g
].prev
= gnextgrpid
;
290 grpsession
[g
].state
= GROUPEOPEN
;
292 grpsession
[g
].sesslist
[i
].sid
= s
;
293 grpsession
[g
].sesslist
[i
].weight
= weight
;
294 grpsession
[g
].nbsession
++;
301 // Add a route to a group
303 static int grp_addroute(groupidt g
, sessionidt s
, in_addr_t ip
, int prefixlen
)
307 for (i
= 0; i
< MAXROUTEINGRP
; i
++)
309 if ((i
>= grpsession
[g
].nbroutesgrp
))
311 LOG(3, s
, session
[s
].tunnel
, " Radius reply Group %d contains route for %s/%d\n",
312 g
, fmtaddr(htonl(ip
), 0), prefixlen
);
314 grpsession
[g
].route
[i
].ip
= ip
;
315 grpsession
[g
].route
[i
].prefixlen
= prefixlen
;
316 grpsession
[g
].nbroutesgrp
++;
319 else if ((grpsession
[g
].route
[i
].ip
== ip
) && (grpsession
[g
].route
[i
].prefixlen
== prefixlen
))
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
);
328 else if (grpsession
[g
].route
[i
].ip
== 0)
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
);
333 grpsession
[g
].route
[i
].ip
= ip
;
334 grpsession
[g
].route
[i
].prefixlen
= prefixlen
;
339 if (i
>= MAXROUTEINGRP
)
341 LOG(1, s
, session
[s
].tunnel
, " Too many routes for Group %d\n", g
);
346 // Process Sames vendor specific attribut radius
347 void grp_processvendorspecific(sessionidt s
, uint8_t *pvs
)
349 uint8_t attrib
= *pvs
;
351 uint8_t *n
= pvs
+ 2;
352 uint8_t *e
= pvs
+ pvs
[1];
354 if ((attrib
>= 22) && (attrib
<= 23))
356 while (n
< e
&& isdigit(*n
))
358 grpid
= grpid
* 10 + *n
- '0';
361 if ((grpid
== 0) || (grpid
>= MAXGROUPE
))
363 LOG(1, s
, session
[s
].tunnel
, " Group Attribute Id %d not allowed\n", grpid
);
368 LOG(1, s
, session
[s
].tunnel
, " Group Attribute Id not defined\n");
372 if (!grp_addsession(grpid
, s
, 1))
377 if (grpid
> config
->cluster_highest_groupeid
)
378 config
->cluster_highest_groupeid
= grpid
;
383 //process, Sames vendor-specific 64520
384 if (attrib
== 22) //SAMES-Group-Framed-Route
390 while (n
< e
&& (isdigit(*n
) || *n
== '.'))
398 u
= u
* 10 + *n
- '0';
405 while (n
< e
&& isdigit(*n
))
406 bits
= bits
* 10 + *n
++ - '0';
408 else if ((ip
>> 24) < 128)
410 else if ((ip
>> 24) < 192)
415 if (!grp_addroute(grpid
, s
, ip
, bits
))
418 else if (attrib
== 23) //SAMES-Group-Session-Weight
422 while (n
< e
&& isdigit(*n
))
423 weight
= weight
* 10 + *n
++ - '0';
427 LOG(1, s
, session
[s
].tunnel
, " Group-Session-Weight 0 GroupId %d not allowed\n", grpid
);
430 if (!grp_addsession(grpid
, s
, weight
))
437 LOG(3, s
, session
[s
].tunnel
, " Unknown vendor-specific: 64520, Attrib: %d\n", attrib
);
441 // Init data structures
446 // Set default value (10s)
447 config
->grp_txrate_average_time
= 10;
449 if (!(grpsession
= shared_malloc(sizeof(groupsesst
) * MAXGROUPE
)))
451 LOG(0, 0, 0, "Error doing malloc for grouped session: %s\n", strerror(errno
));
455 memset(grpsession
, 0, sizeof(grpsession
[0]) * MAXGROUPE
);
456 for (i
= 1; i
< MAXGROUPE
; i
++)
458 grpsession
[i
].state
= GROUPEUNDEF
;
461 if (!(grp_local
= shared_malloc(sizeof(local_group
) * MAXGROUPE
)))
463 LOG(0, 0, 0, "Error doing malloc for grp_local: %s\n", strerror(errno
));
466 memset(grp_local
, 0, sizeof(grp_local
[0]) * MAXGROUPE
);
470 // Update time_changed of the group
471 void grp_time_changed()
475 for (g
= gnextgrpid
; g
!= 0; g
= grpsession
[g
].prev
)
477 grpsession
[g
].time_changed
++;
481 // Uncache all IP of a session
482 static void grp_uncache_ipsession(groupidt g
, sessionidt s
)
491 for (i
= 0; i
< grpsession
[g
].nbroutesgrp
; i
++)
493 if (grpsession
[g
].route
[i
].ip
!= 0)
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.
498 for (j
= ip
; j
< ip
+(1<<(32-prefixlen
)) ; ++j
)
500 n_ip
= htonl(j
); // To network order
501 a
= (uint8_t *) &n_ip
;
504 if (!(h
= h
[*a
++].idx
)) continue;
505 if (!(h
= h
[*a
++].idx
)) continue;
506 if (!(h
= h
[*a
++].idx
)) continue;
511 //LOG(3, s, session[s].tunnel, "UnCaching ip address %s\n", fmtaddr(n_ip, 0));
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
)
522 sessionidt s
= 0, s2
= 0, s3
= 0;
524 uint32_t ltime_changed
= 0, mintxrate
= 0xFFFFFFFF, maxtxrate
= 0;
530 if (grpsession
[g
].time_changed
>= config
->grp_txrate_average_time
)
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
++)
537 if ((s2
= grpsession
[g
].sesslist
[i
].sid
))
539 uint32_t coutgrp_delta
= 0;
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
;
545 txrate
= (txrate
+ (coutgrp_delta
/ltime_changed
)) >> 1;
546 grpsession
[g
].sesslist
[i
].tx_rate
= txrate
;
548 txrate
= grpsession
[g
].sesslist
[i
].tx_rate
/grpsession
[g
].sesslist
[i
].weight
;
549 if (txrate
< mintxrate
)
551 if ( session
[s2
].ppp
.phase
> Establish
&&
552 (time_now
- session
[s2
].last_packet
<= (config
->echo_timeout
+ 1)) )
554 grpsession
[g
].smin
= s2
;
559 if (txrate
> maxtxrate
)
561 if ( session
[s2
].ppp
.phase
> Establish
&&
562 (time_now
- session
[s2
].last_packet
<= (config
->echo_timeout
+ 1)) )
564 grpsession
[g
].smax
= s2
;
572 if ((s
= sessionbyip(ip
)))
574 uint8_t *as
= (uint8_t *) &ip_src
;
575 uint8_t *ad
= (uint8_t *) &ip
;
580 s
= grp_local
[g
].sid_loaddist
[ai
];
583 s
= grpsession
[g
].smin
;
584 grp_local
[g
].sid_loaddist
[ai
] = s
;
587 if (g
!= grp_groupbysession(s
))
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
);
592 grp_local
[g
].sid_loaddist
[ai
] = 0;
594 else if ( (session
[s
].ppp
.phase
> Establish
) &&
595 (time_now
- session
[s
].last_packet
<= (config
->echo_timeout
+ 1)) )
597 grp_local
[g
].sid_loaddist
[guint16_index_loadlist
++] = 0;
603 grp_local
[g
].sid_loaddist
[ai
] = 0;
609 // random between 0 and nbsession-1
610 uint indexsess
= (rand() % grpsession
[g
].nbsession
);
612 if (indexsess
>= grpsession
[g
].nbsession
)
613 indexsess
= 0; //Sanity checks.
615 s2
= grpsession
[g
].sesslist
[indexsess
].sid
;
617 (session
[s2
].ppp
.phase
> Establish
) &&
618 (time_now
- session
[s2
].last_packet
<= (config
->echo_timeout
+ 1)))
624 for (i
= 0; i
< grpsession
[g
].nbsession
; i
++)
626 if ((s2
= grpsession
[g
].sesslist
[i
].sid
))
630 if ( session
[s2
].ppp
.phase
> Establish
&&
631 (time_now
- session
[s2
].last_packet
<= (config
->echo_timeout
+ 1)) )
645 cache_ipmap(ntohl(ip
), s
);
650 // load a groupe receive from master
651 int grp_cluster_load_groupe(groupidt g
, groupsesst
*new)
658 LOG(0, 0, 0, "ERROR: Received a group id > MAXGROUPE!\n");
662 if ((grpsession
[g
].nbroutesgrp
!= new->nbroutesgrp
) ||
663 (grpsession
[g
].nbsession
!= new->nbsession
))
670 // Check session list
671 for (i
= 0; i
< grpsession
[g
].nbsession
; i
++)
673 if (grpsession
[g
].sesslist
[i
].sid
!= new->sesslist
[i
].sid
)
684 for (i
= 0; i
< grpsession
[g
].nbroutesgrp
; i
++)
686 if (grpsession
[g
].route
[i
].ip
!= new->route
[i
].ip
)
698 grp_setgrouproute(g
, 0);
701 memcpy(&grpsession
[g
], new, sizeof(grpsession
[g
])); // Copy over..
707 grp_setgrouproute(g
, 1);