3 * Grouped session for load balancing and fail-over
10 #include <sys/socket.h>
11 #include <linux/rtnetlink.h>
22 union grp_iphash
*idx
;
23 } grp_ip_hash
[256]; // Mapping from IP address to group structures.
25 static groupidt gnextgrpid
= 0;
27 // Find gruop by IP, < 1 for not found
29 // Confusingly enough, this 'ip' must be
30 // in _network_ order. This being the common
31 // case when looking it up from IP packet headers.
32 static groupidt
grp_lookup_ipmap(in_addr_t ip
)
34 uint8_t *a
= (uint8_t *) &ip
;
35 union grp_iphash
*h
= grp_ip_hash
;
37 if (!(h
= h
[*a
++].idx
)) return 0;
38 if (!(h
= h
[*a
++].idx
)) return 0;
39 if (!(h
= h
[*a
++].idx
)) return 0;
45 // Take an IP address in HOST byte order and
46 // add it to the grouid by IP cache.
48 // (It's actually cached in network order)
50 static void grp_cache_ipmap(in_addr_t ip
, groupidt g
)
52 in_addr_t nip
= htonl(ip
); // MUST be in network order. I.e. MSB must in be ((char *) (&ip))[0]
53 uint8_t *a
= (uint8_t *) &nip
;
54 union grp_iphash
*h
= grp_ip_hash
;
57 for (i
= 0; i
< 3; i
++)
59 if (!(h
[a
[i
]].idx
|| (h
[a
[i
]].idx
= calloc(256, sizeof(union grp_iphash
)))))
68 LOG(4, 0, 0, "Caching Group:%d ip address %s\n", g
, fmtaddr(nip
, 0));
70 LOG(4, 0, 0, "Un-caching Group ip address %s\n", fmtaddr(nip
, 0));
73 groupidt
grp_groupbyip(in_addr_t ip
)
75 groupidt g
= grp_lookup_ipmap(ip
);
77 if (g
> 0 && g
< MAXGROUPE
)
85 // This adds it to the routing table, advertises it
86 // via BGP if enabled, and stuffs it into the
87 // 'sessionbyip' cache.
89 // 'ip' must be in _host_ order.
91 static void grp_routeset(groupidt g
, in_addr_t ip
, int prefixlen
, int add
)
101 if (!prefixlen
) prefixlen
= 32;
103 ip
&= 0xffffffff << (32 - prefixlen
);; // Force the ip to be the first one in the route.
105 memset(&req
, 0, sizeof(req
));
109 req
.nh
.nlmsg_type
= RTM_NEWROUTE
;
110 req
.nh
.nlmsg_flags
= NLM_F_REQUEST
| NLM_F_CREATE
| NLM_F_REPLACE
;
114 req
.nh
.nlmsg_type
= RTM_DELROUTE
;
115 req
.nh
.nlmsg_flags
= NLM_F_REQUEST
;
118 req
.nh
.nlmsg_len
= NLMSG_LENGTH(sizeof(req
.rt
));
120 req
.rt
.rtm_family
= AF_INET
;
121 req
.rt
.rtm_dst_len
= prefixlen
;
122 req
.rt
.rtm_table
= RT_TABLE_MAIN
;
123 req
.rt
.rtm_protocol
= 42;
124 req
.rt
.rtm_scope
= RT_SCOPE_LINK
;
125 req
.rt
.rtm_type
= RTN_UNICAST
;
127 netlink_addattr(&req
.nh
, RTA_OIF
, &tunidx
, sizeof(int));
129 netlink_addattr(&req
.nh
, RTA_DST
, &n_ip
, sizeof(n_ip
));
131 LOG(1, 0, 0, "Route (Group) %s %s/%d\n", add
? "add" : "del", fmtaddr(htonl(ip
), 0), prefixlen
);
133 if (netlink_send(&req
.nh
) < 0)
134 LOG(0, 0, 0, "grp_routeset() error in sending netlink message: %s\n", strerror(errno
));
138 bgp_add_route(htonl(ip
), prefixlen
);
140 bgp_del_route(htonl(ip
), prefixlen
);
143 // Add/Remove the IPs to the 'groupbyip' cache.
144 // Note that we add the zero address in the case of
145 // a network route. Roll on CIDR.
147 // Note that 'g == 0' implies this is the address pool.
148 // We still cache it here, because it will pre-fill
149 // the malloc'ed tree.
152 if (!add
) // Are we deleting a route?
153 g
= 0; // Caching the session as '0' is the same as uncaching.
155 for (i
= ip
; i
< ip
+(1<<(32-prefixlen
)) ; ++i
)
156 grp_cache_ipmap(i
, g
);
160 // Set all route of a group
161 void grp_setgrouproute(groupidt g
, int add
)
164 for (i
= 0; i
< grpsession
[g
].nbroutesgrp
; i
++)
166 if (grpsession
[g
].route
[i
].ip
!= 0)
168 grp_routeset(g
, grpsession
[g
].route
[i
].ip
, grpsession
[g
].route
[i
].prefixlen
, add
);
173 // return group id by session
174 groupidt
grp_groupbysession(sessionidt s
)
178 for (g
= gnextgrpid
; g
!= 0; g
= grpsession
[g
].prev
)
180 for (i
= 0; i
< grpsession
[g
].nbsession
; i
++)
182 if (grpsession
[g
].sesslist
[i
].sid
== s
)
183 { // session found in group
192 // Remove a session to a group
194 void grp_removesession(groupidt g
, sessionidt s
)
198 if (grpsession
[g
].nbsession
<= 0)
201 for (i
= 0; i
< grpsession
[g
].nbsession
; i
++)
203 if (grpsession
[g
].sesslist
[i
].sid
== s
)
204 { // session found on group
205 --grpsession
[g
].nbsession
;
206 if (grpsession
[g
].nbsession
== 0)
208 // Group is empty, remove it
211 grp_setgrouproute(g
, 0);
220 for (g2
= gnextgrpid
; g2
!= 0; g2
= grpsession
[g2
].prev
)
222 if (grpsession
[g2
].prev
== g
)
224 grpsession
[g2
].prev
= grpsession
[g
].prev
;
230 memset(&grpsession
[g
], 0, sizeof(grpsession
[0]));
234 // remove the session
235 memmove(&grpsession
[g
].sesslist
[i
],
236 &grpsession
[g
].sesslist
[i
+1],
237 (grpsession
[g
].nbsession
- i
) * sizeof(grpsession
[g
].sesslist
[i
]));
244 // Add a session to a group
246 static int grp_addsession(groupidt g
, sessionidt s
, uint8_t weight
)
250 for (i
= 0; i
< grpsession
[g
].nbsession
; i
++)
252 if (grpsession
[g
].sesslist
[i
].sid
== s
)
253 { // already in group
254 if ((!grpsession
[g
].sesslist
[i
].weight
) || (weight
> 1))
255 grpsession
[g
].sesslist
[i
].weight
= weight
; // update Weight session (for load-balancing)
261 if (i
>= MAXSESSINGRP
)
263 LOG(1, s
, session
[s
].tunnel
, " Too many session for Group %d\n", g
);
267 { // add session id to group
270 // it's the first session of the group, set to next group
271 grpsession
[g
].prev
= gnextgrpid
;
274 grpsession
[g
].sesslist
[i
].sid
= s
;
275 grpsession
[g
].sesslist
[i
].weight
= weight
;
276 grpsession
[g
].nbsession
++;
283 // Add a route to a group
285 static int grp_addroute(groupidt g
, sessionidt s
, in_addr_t ip
, int prefixlen
)
289 for (i
= 0; i
< MAXROUTEINGRP
; i
++)
291 if ((i
>= grpsession
[g
].nbroutesgrp
))
293 LOG(3, s
, session
[s
].tunnel
, " Radius reply Group %d contains route for %s/%d\n",
294 g
, fmtaddr(htonl(ip
), 0), prefixlen
);
296 grpsession
[g
].route
[i
].ip
= ip
;
297 grpsession
[g
].route
[i
].prefixlen
= prefixlen
;
298 grpsession
[g
].nbroutesgrp
++;
301 else if ((grpsession
[g
].route
[i
].ip
== ip
) && (grpsession
[g
].route
[i
].prefixlen
== prefixlen
))
303 // route already defined in group
304 LOG(3, s
, session
[s
].tunnel
,
305 " Radius reply Group %d contains route for %s/%d (this already defined)\n",
306 g
, fmtaddr(htonl(ip
), 0), prefixlen
);
310 else if (grpsession
[g
].route
[i
].ip
== 0)
312 LOG(3, s
, session
[s
].tunnel
, " Radius reply Group %d contains route for %s/%d (find empty on list!!!)\n",
313 g
, fmtaddr(htonl(ip
), 0), prefixlen
);
315 grpsession
[g
].route
[i
].ip
= ip
;
316 grpsession
[g
].route
[i
].prefixlen
= prefixlen
;
321 if (i
>= MAXROUTEINGRP
)
323 LOG(1, s
, session
[s
].tunnel
, " Too many routes for Group %d\n", g
);
328 // Process Sames vendor specific attribut radius
329 void grp_processvendorspecific(sessionidt s
, uint8_t *pvs
)
331 uint8_t attrib
= *pvs
;
333 uint8_t *n
= pvs
+ 2;
334 uint8_t *e
= pvs
+ pvs
[1];
336 if ((attrib
>= 22) && (attrib
<= 23))
338 while (n
< e
&& isdigit(*n
))
340 grpid
= grpid
* 10 + *n
- '0';
343 if ((grpid
== 0) || (grpid
>= MAXGROUPE
))
345 LOG(1, s
, session
[s
].tunnel
, " Group Attribute Id %d not allowed\n", grpid
);
350 LOG(1, s
, session
[s
].tunnel
, " Group Attribute Id not defined\n");
354 if (!grp_addsession(grpid
, s
, 1))
362 //process, Sames vendor-specific 64520
363 if (attrib
== 22) //SAMES-Group-Framed-Route
369 while (n
< e
&& (isdigit(*n
) || *n
== '.'))
377 u
= u
* 10 + *n
- '0';
384 while (n
< e
&& isdigit(*n
))
385 bits
= bits
* 10 + *n
++ - '0';
387 else if ((ip
>> 24) < 128)
389 else if ((ip
>> 24) < 192)
394 if (!grp_addroute(grpid
, s
, ip
, bits
))
397 else if (attrib
== 23) //SAMES-Group-Session-Weight
401 while (n
< e
&& isdigit(*n
))
402 weight
= weight
* 10 + *n
++ - '0';
406 LOG(1, s
, session
[s
].tunnel
, " Group-Session-Weight 0 GroupId %d not allowed\n", grpid
);
409 if (!grp_addsession(grpid
, s
, weight
))
416 LOG(3, s
, session
[s
].tunnel
, " Unknown vendor-specific: 64520, Attrib: %d\n", attrib
);
420 // Init data structures
423 // Set default value (10s)
424 config
->grp_txrate_average_time
= 10;
426 if (!(grpsession
= shared_malloc(sizeof(groupsesst
) * MAXGROUPE
)))
428 LOG(0, 0, 0, "Error doing malloc for grouped session: %s\n", strerror(errno
));
432 memset(grpsession
, 0, sizeof(grpsession
[0]) * MAXGROUPE
);
435 // Update time_changed of the group
436 void grp_time_changed()
440 for (g
= gnextgrpid
; g
!= 0; g
= grpsession
[g
].prev
)
442 grpsession
[g
].time_changed
++;
446 // Uncache all IP of a session
447 static void grp_uncache_ipsession(groupidt g
, sessionidt s
)
456 for (i
= 0; i
< grpsession
[g
].nbroutesgrp
; i
++)
458 if (grpsession
[g
].route
[i
].ip
!= 0)
460 prefixlen
= grpsession
[g
].route
[i
].prefixlen
;
461 ip
= grpsession
[g
].route
[i
].ip
& (0xffffffff << (32 - prefixlen
)); // Force the ip to be the first one in the route.
463 for (j
= ip
; j
< ip
+(1<<(32-prefixlen
)) ; ++j
)
465 n_ip
= htonl(j
); // To network order
466 a
= (uint8_t *) &n_ip
;
469 if (!(h
= h
[*a
++].idx
)) continue;
470 if (!(h
= h
[*a
++].idx
)) continue;
471 if (!(h
= h
[*a
++].idx
)) continue;
476 //LOG(3, s, session[s].tunnel, "UnCaching ip address %s\n", fmtaddr(n_ip, 0));
483 // return the next session can be used on the group
484 sessionidt
grp_getnextsession(groupidt g
, in_addr_t ip
)
486 sessionidt s
= 0, s2
= 0, s3
= 0, smax
= 0;
488 uint32_t ltime_changed
= 0, mintxrate
= 0xFFFFFFFF, maxtxrate
= 0;
494 if (grpsession
[g
].time_changed
< config
->grp_txrate_average_time
)
496 if ((s
= sessionbyip(ip
)))
498 if ( (session
[s
].ppp
.phase
> Establish
) &&
499 (time_now
- session
[s
].last_packet
<= (config
->echo_timeout
+ 1)) )
508 // recalculation txrate
509 ltime_changed
= grpsession
[g
].time_changed
;
510 grpsession
[g
].time_changed
= 0;
512 for (i
= 0; i
< grpsession
[g
].nbsession
; i
++)
514 if ((s2
= grpsession
[g
].sesslist
[i
].sid
))
518 grpsession
[g
].sesslist
[i
].tx_rate
= session
[s2
].coutgrp_delta
/ltime_changed
;
519 session
[s2
].coutgrp_delta
= 0;
521 //LOG(3, s2, session[s2].tunnel, "TX Rate: %d session weight: %d\n",
522 // grpsession[g].sesslist[i].tx_rate, grpsession[g].sesslist[i].weight);
524 txrate
= grpsession
[g
].sesslist
[i
].tx_rate
/grpsession
[g
].sesslist
[i
].weight
;
525 if (txrate
< mintxrate
)
527 if ( session
[s2
].ppp
.phase
> Establish
&&
528 (time_now
- session
[s2
].last_packet
<= (config
->echo_timeout
+ 1)) )
535 if (txrate
> maxtxrate
)
537 if ( session
[s2
].ppp
.phase
> Establish
&&
538 (time_now
- session
[s2
].last_packet
<= (config
->echo_timeout
+ 1)) )
547 if (smax
&& (maxtxrate
!= mintxrate
))
549 grp_uncache_ipsession(g
, smax
);
555 // random between 0 and nbsession-1
556 uint indexsess
= (rand() % grpsession
[g
].nbsession
);
558 if (indexsess
>= grpsession
[g
].nbsession
)
559 indexsess
= 0; //Sanity checks.
561 s2
= grpsession
[g
].sesslist
[indexsess
].sid
;
563 (session
[s2
].ppp
.phase
> Establish
) &&
564 (time_now
- session
[s2
].last_packet
<= (config
->echo_timeout
+ 1)))
567 //LOG(3, s, session[s].tunnel, "New random session\n");
571 for (i
= 0; i
< grpsession
[g
].nbsession
; i
++)
573 if ((s2
= grpsession
[g
].sesslist
[i
].sid
))
577 if ( session
[s2
].ppp
.phase
> Establish
&&
578 (time_now
- session
[s2
].last_packet
<= (config
->echo_timeout
+ 1)) )
592 cache_ipmap(ntohl(ip
), s
);