73c729a39321be9fae93358a82440254601c784c
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;
46 // Take an IP address in HOST byte order and
47 // add it to the grouid by IP cache.
49 // (It's actually cached in network order)
51 static void grp_cache_ipmap(in_addr_t ip
, groupidt g
)
53 in_addr_t nip
= htonl(ip
); // MUST be in network order. I.e. MSB must in be ((char *) (&ip))[0]
54 uint8_t *a
= (uint8_t *) &nip
;
55 union grp_iphash
*h
= grp_ip_hash
;
58 for (i
= 0; i
< 3; i
++)
60 if (!(h
[a
[i
]].idx
|| (h
[a
[i
]].idx
= calloc(256, sizeof(union grp_iphash
)))))
69 LOG(4, 0, 0, "Caching Group:%d ip address %s\n", g
, fmtaddr(nip
, 0));
71 LOG(4, 0, 0, "Un-caching Group ip address %s\n", fmtaddr(nip
, 0));
74 groupidt
grp_groupbyip(in_addr_t ip
)
76 groupidt g
= grp_lookup_ipmap(ip
);
78 if (g
> 0 && g
< MAXGROUPE
)
86 // This adds it to the routing table, advertises it
87 // via BGP if enabled, and stuffs it into the
90 // 'ip' must be in _host_ order.
92 static void grp_routeset(groupidt g
, in_addr_t ip
, int prefixlen
, int add
)
102 if (!prefixlen
) prefixlen
= 32;
104 ip
&= 0xffffffff << (32 - prefixlen
);; // Force the ip to be the first one in the route.
106 memset(&req
, 0, sizeof(req
));
110 req
.nh
.nlmsg_type
= RTM_NEWROUTE
;
111 req
.nh
.nlmsg_flags
= NLM_F_REQUEST
| NLM_F_CREATE
| NLM_F_REPLACE
;
115 req
.nh
.nlmsg_type
= RTM_DELROUTE
;
116 req
.nh
.nlmsg_flags
= NLM_F_REQUEST
;
119 req
.nh
.nlmsg_len
= NLMSG_LENGTH(sizeof(req
.rt
));
121 req
.rt
.rtm_family
= AF_INET
;
122 req
.rt
.rtm_dst_len
= prefixlen
;
123 req
.rt
.rtm_table
= RT_TABLE_MAIN
;
124 req
.rt
.rtm_protocol
= 42;
125 req
.rt
.rtm_scope
= RT_SCOPE_LINK
;
126 req
.rt
.rtm_type
= RTN_UNICAST
;
128 netlink_addattr(&req
.nh
, RTA_OIF
, &tunidx
, sizeof(int));
130 netlink_addattr(&req
.nh
, RTA_DST
, &n_ip
, sizeof(n_ip
));
132 LOG(1, 0, 0, "Route (Group) %s %s/%d\n", add
? "add" : "del", fmtaddr(htonl(ip
), 0), prefixlen
);
134 if (netlink_send(&req
.nh
) < 0)
135 LOG(0, 0, 0, "grp_routeset() error in sending netlink message: %s\n", strerror(errno
));
139 bgp_add_route(htonl(ip
), prefixlen
);
141 bgp_del_route(htonl(ip
), prefixlen
);
144 // Add/Remove the IPs to the 'groupbyip' cache.
145 // Note that we add the zero address in the case of
146 // a network route. Roll on CIDR.
148 // Note that 'g == 0' implies this is the address pool.
149 // We still cache it here, because it will pre-fill
150 // the malloc'ed tree.
153 if (!add
) // Are we deleting a route?
154 g
= 0; // Caching the session as '0' is the same as uncaching.
156 for (i
= ip
; i
< ip
+(1<<(32-prefixlen
)) ; ++i
)
158 grp_cache_ipmap(i
, g
);
159 if (!g
) cache_ipmap(i
, 0);
164 // Set all route of a group
165 void grp_setgrouproute(groupidt g
, int add
)
168 for (i
= 0; i
< grpsession
[g
].nbroutesgrp
; i
++)
170 if (grpsession
[g
].route
[i
].ip
!= 0)
172 grp_routeset(g
, grpsession
[g
].route
[i
].ip
, grpsession
[g
].route
[i
].prefixlen
, add
);
177 // return group id by session
178 groupidt
grp_groupbysession(sessionidt s
)
182 for (g
= gnextgrpid
; g
!= 0; g
= grpsession
[g
].prev
)
184 for (i
= 0; i
< grpsession
[g
].nbsession
; i
++)
186 if (grpsession
[g
].sesslist
[i
].sid
== s
)
187 { // session found in group
196 // Remove a session to a group
198 void grp_removesession(groupidt g
, sessionidt s
)
202 if (grpsession
[g
].nbsession
<= 0)
205 for (i
= 0; i
< grpsession
[g
].nbsession
; i
++)
207 if (grpsession
[g
].sesslist
[i
].sid
== s
)
208 { // session found on group
209 --grpsession
[g
].nbsession
;
210 if (grpsession
[g
].nbsession
== 0)
212 // Group is empty, remove it
215 grp_setgrouproute(g
, 0);
219 gnextgrpid
= grpsession
[g
].prev
;
224 for (g2
= gnextgrpid
; g2
!= 0; g2
= grpsession
[g2
].prev
)
226 if (grpsession
[g2
].prev
== g
)
228 grpsession
[g2
].prev
= grpsession
[g
].prev
;
234 memset(&grpsession
[g
], 0, sizeof(grpsession
[0]));
235 grpsession
[g
].state
= GROUPEFREE
;
239 // remove the session
240 memmove(&grpsession
[g
].sesslist
[i
],
241 &grpsession
[g
].sesslist
[i
+1],
242 (grpsession
[g
].nbsession
- i
) * sizeof(grpsession
[g
].sesslist
[i
]));
245 cluster_send_groupe(g
);
251 // Add a session to a group
253 static int grp_addsession(groupidt g
, sessionidt s
, uint8_t weight
)
257 for (i
= 0; i
< grpsession
[g
].nbsession
; i
++)
259 if (grpsession
[g
].sesslist
[i
].sid
== s
)
260 { // already in group
261 if ((!grpsession
[g
].sesslist
[i
].weight
) || (weight
> 1))
262 grpsession
[g
].sesslist
[i
].weight
= weight
; // update Weight session (for load-balancing)
268 if (i
>= MAXSESSINGRP
)
270 LOG(1, s
, session
[s
].tunnel
, " Too many session for Group %d\n", g
);
274 { // add session id to group
277 // it's the first session of the group, set to next group
278 grpsession
[g
].prev
= gnextgrpid
;
280 grpsession
[g
].state
= GROUPEOPEN
;
282 grpsession
[g
].sesslist
[i
].sid
= s
;
283 grpsession
[g
].sesslist
[i
].weight
= weight
;
284 grpsession
[g
].nbsession
++;
291 // Add a route to a group
293 static int grp_addroute(groupidt g
, sessionidt s
, in_addr_t ip
, int prefixlen
)
297 for (i
= 0; i
< MAXROUTEINGRP
; i
++)
299 if ((i
>= grpsession
[g
].nbroutesgrp
))
301 LOG(3, s
, session
[s
].tunnel
, " Radius reply Group %d contains route for %s/%d\n",
302 g
, fmtaddr(htonl(ip
), 0), prefixlen
);
304 grpsession
[g
].route
[i
].ip
= ip
;
305 grpsession
[g
].route
[i
].prefixlen
= prefixlen
;
306 grpsession
[g
].nbroutesgrp
++;
309 else if ((grpsession
[g
].route
[i
].ip
== ip
) && (grpsession
[g
].route
[i
].prefixlen
== prefixlen
))
311 // route already defined in group
312 LOG(3, s
, session
[s
].tunnel
,
313 " Radius reply Group %d contains route for %s/%d (this already defined)\n",
314 g
, fmtaddr(htonl(ip
), 0), prefixlen
);
318 else if (grpsession
[g
].route
[i
].ip
== 0)
320 LOG(3, s
, session
[s
].tunnel
, " Radius reply Group %d contains route for %s/%d (find empty on list!!!)\n",
321 g
, fmtaddr(htonl(ip
), 0), prefixlen
);
323 grpsession
[g
].route
[i
].ip
= ip
;
324 grpsession
[g
].route
[i
].prefixlen
= prefixlen
;
329 if (i
>= MAXROUTEINGRP
)
331 LOG(1, s
, session
[s
].tunnel
, " Too many routes for Group %d\n", g
);
336 // Process Sames vendor specific attribut radius
337 void grp_processvendorspecific(sessionidt s
, uint8_t *pvs
)
339 uint8_t attrib
= *pvs
;
341 uint8_t *n
= pvs
+ 2;
342 uint8_t *e
= pvs
+ pvs
[1];
344 if ((attrib
>= 22) && (attrib
<= 23))
346 while (n
< e
&& isdigit(*n
))
348 grpid
= grpid
* 10 + *n
- '0';
351 if ((grpid
== 0) || (grpid
>= MAXGROUPE
))
353 LOG(1, s
, session
[s
].tunnel
, " Group Attribute Id %d not allowed\n", grpid
);
358 LOG(1, s
, session
[s
].tunnel
, " Group Attribute Id not defined\n");
362 if (!grp_addsession(grpid
, s
, 1))
367 if (grpid
> config
->cluster_highest_groupeid
)
368 config
->cluster_highest_groupeid
= grpid
;
373 //process, Sames vendor-specific 64520
374 if (attrib
== 22) //SAMES-Group-Framed-Route
380 while (n
< e
&& (isdigit(*n
) || *n
== '.'))
388 u
= u
* 10 + *n
- '0';
395 while (n
< e
&& isdigit(*n
))
396 bits
= bits
* 10 + *n
++ - '0';
398 else if ((ip
>> 24) < 128)
400 else if ((ip
>> 24) < 192)
405 if (!grp_addroute(grpid
, s
, ip
, bits
))
408 else if (attrib
== 23) //SAMES-Group-Session-Weight
412 while (n
< e
&& isdigit(*n
))
413 weight
= weight
* 10 + *n
++ - '0';
417 LOG(1, s
, session
[s
].tunnel
, " Group-Session-Weight 0 GroupId %d not allowed\n", grpid
);
420 if (!grp_addsession(grpid
, s
, weight
))
427 LOG(3, s
, session
[s
].tunnel
, " Unknown vendor-specific: 64520, Attrib: %d\n", attrib
);
431 // Init data structures
436 // Set default value (10s)
437 config
->grp_txrate_average_time
= 10;
439 if (!(grpsession
= shared_malloc(sizeof(groupsesst
) * MAXGROUPE
)))
441 LOG(0, 0, 0, "Error doing malloc for grouped session: %s\n", strerror(errno
));
445 memset(grpsession
, 0, sizeof(grpsession
[0]) * MAXGROUPE
);
446 for (i
= 1; i
< MAXGROUPE
; i
++)
448 grpsession
[i
].state
= GROUPEUNDEF
;
452 // Update time_changed of the group
453 void grp_time_changed()
457 for (g
= gnextgrpid
; g
!= 0; g
= grpsession
[g
].prev
)
459 grpsession
[g
].time_changed
++;
463 // Uncache all IP of a session
464 static void grp_uncache_ipsession(groupidt g
, sessionidt s
)
473 for (i
= 0; i
< grpsession
[g
].nbroutesgrp
; i
++)
475 if (grpsession
[g
].route
[i
].ip
!= 0)
477 prefixlen
= grpsession
[g
].route
[i
].prefixlen
;
478 ip
= grpsession
[g
].route
[i
].ip
& (0xffffffff << (32 - prefixlen
)); // Force the ip to be the first one in the route.
480 for (j
= ip
; j
< ip
+(1<<(32-prefixlen
)) ; ++j
)
482 n_ip
= htonl(j
); // To network order
483 a
= (uint8_t *) &n_ip
;
486 if (!(h
= h
[*a
++].idx
)) continue;
487 if (!(h
= h
[*a
++].idx
)) continue;
488 if (!(h
= h
[*a
++].idx
)) continue;
493 //LOG(3, s, session[s].tunnel, "UnCaching ip address %s\n", fmtaddr(n_ip, 0));
500 // return the next session can be used on the group
501 sessionidt
grp_getnextsession(groupidt g
, in_addr_t ip
)
503 sessionidt s
= 0, s2
= 0, s3
= 0;
505 uint32_t ltime_changed
= 0, mintxrate
= 0xFFFFFFFF, maxtxrate
= 0;
511 if (grpsession
[g
].time_changed
>= config
->grp_txrate_average_time
)
513 // recalculation txrate
514 ltime_changed
= grpsession
[g
].time_changed
;
515 grpsession
[g
].time_changed
= 0;
516 for (i
= 0; i
< grpsession
[g
].nbsession
; i
++)
518 if ((s2
= grpsession
[g
].sesslist
[i
].sid
))
520 uint32_t coutgrp_delta
= 0;
522 if (session
[s2
].cout
>= grpsession
[g
].sesslist
[i
].prev_coutgrp
)
523 coutgrp_delta
= session
[s2
].cout
- grpsession
[g
].sesslist
[i
].prev_coutgrp
;
524 grpsession
[g
].sesslist
[i
].prev_coutgrp
= session
[s2
].cout
;
526 grpsession
[g
].sesslist
[i
].tx_rate
= coutgrp_delta
/ltime_changed
;
528 txrate
= grpsession
[g
].sesslist
[i
].tx_rate
/grpsession
[g
].sesslist
[i
].weight
;
529 if (txrate
< mintxrate
)
531 if ( session
[s2
].ppp
.phase
> Establish
&&
532 (time_now
- session
[s2
].last_packet
<= (config
->echo_timeout
+ 1)) )
534 grpsession
[g
].smin
= s2
;
539 if (txrate
> maxtxrate
)
541 if ( session
[s2
].ppp
.phase
> Establish
&&
542 (time_now
- session
[s2
].last_packet
<= (config
->echo_timeout
+ 1)) )
544 grpsession
[g
].smax
= s2
;
552 if ((s
= sessionbyip(ip
)))
554 if (s
== grpsession
[g
].smax
)
556 s
= grpsession
[g
].smin
;
557 grpsession
[g
].smax
= 0;
559 else if ( (session
[s
].ppp
.phase
> Establish
) &&
560 (time_now
- session
[s
].last_packet
<= (config
->echo_timeout
+ 1)) )
572 // random between 0 and nbsession-1
573 uint indexsess
= (rand() % grpsession
[g
].nbsession
);
575 if (indexsess
>= grpsession
[g
].nbsession
)
576 indexsess
= 0; //Sanity checks.
578 s2
= grpsession
[g
].sesslist
[indexsess
].sid
;
580 (session
[s2
].ppp
.phase
> Establish
) &&
581 (time_now
- session
[s2
].last_packet
<= (config
->echo_timeout
+ 1)))
587 for (i
= 0; i
< grpsession
[g
].nbsession
; i
++)
589 if ((s2
= grpsession
[g
].sesslist
[i
].sid
))
593 if ( session
[s2
].ppp
.phase
> Establish
&&
594 (time_now
- session
[s2
].last_packet
<= (config
->echo_timeout
+ 1)) )
608 cache_ipmap(ntohl(ip
), s
);
613 // load a groupe receive from master
614 int grp_cluster_load_groupe(groupidt g
, groupsesst
*new)
621 LOG(0, 0, 0, "ERROR: Received a group id > MAXGROUPE!\n");
625 if ((grpsession
[g
].nbroutesgrp
!= new->nbroutesgrp
) ||
626 (grpsession
[g
].nbsession
!= new->nbsession
))
633 // Check session list
634 for (i
= 0; i
< grpsession
[g
].nbsession
; i
++)
636 if (grpsession
[g
].sesslist
[i
].sid
== new->sesslist
[i
].sid
)
647 for (i
= 0; i
< grpsession
[g
].nbroutesgrp
; i
++)
649 if (grpsession
[g
].route
[i
].ip
!= new->route
[i
].ip
)
661 grp_setgrouproute(g
, 0);
664 memcpy(&grpsession
[g
], new, sizeof(grpsession
[g
])); // Copy over..
670 grp_setgrouproute(g
, 1);