From: Brendan O'Dea Date: Tue, 28 Jun 2005 14:48:17 +0000 (+0000) Subject: add DAE support (PoD/CoA) from Vladislav Bjelic X-Git-Tag: 2.2.1-2fdn3.1~19^2^2~1^2~149 X-Git-Url: http://git.sameswireless.fr/l2tpns.git/commitdiff_plain/a685501f6432b2cd8cd23ced906abcb2d2ce862f?ds=inline add DAE support (PoD/CoA) from Vladislav Bjelic --- diff --git a/Changes b/Changes index 70c1f61..452407b 100644 --- a/Changes +++ b/Changes @@ -1,9 +1,10 @@ -* Mon Jun 27 2005 Brendan O'Dea 2.1.2 +* Wed Jun 29 2005 Brendan O'Dea 2.1.2 - Don't resend IPCP while still in progress. - Ignore duplicate ACKs for IPCP. - Clear RADIUSIPCP for walled garden sessions on ACK. - Clear cluster_master on election so that slaves will accept a new master. - Provide more comments/defaults in etc/startup-config.default. +- Add DAE support (PoD/CoA) from Vladislav Bjelic. * Tue Jun 14 2005 Brendan O'Dea 2.1.1 - Add missing newline to backtrace macro. diff --git a/Docs/manual.html b/Docs/manual.html index 1395e3a..4166235 100644 --- a/Docs/manual.html +++ b/Docs/manual.html @@ -229,6 +229,11 @@ A comma separated list of supported RADIUS authentication methods (pap or chap), in order of preference (default pap). +
  • radius_dae_port (short)
    +Port for DAE RADIUS (Packet of Death/Disconnect, Change of Authorization) +requests (default: 3799). +
  • +
  • allow_duplicate_users (boolean)
    Allow multiple logins with the same username. If false (the default), any prior session with the same username will be dropped when a new diff --git a/Docs/startup-config.5 b/Docs/startup-config.5 index 61c2dc2..7170dbe 100644 --- a/Docs/startup-config.5 +++ b/Docs/startup-config.5 @@ -2,7 +2,7 @@ .de Id .ds Dt \\$4 \\$5 .. -.Id $Id: startup-config.5,v 1.10 2005-06-02 11:32:33 bodea Exp $ +.Id $Id: startup-config.5,v 1.11 2005-06-28 14:48:31 bodea Exp $ .TH STARTUP-CONFIG 5 "\*(Dt" L2TPNS "File Formats and Conventions" .SH NAME startup\-config \- configuration file for l2tpns @@ -100,6 +100,10 @@ Secret to be used in RADIUS packets. A comma separated list of supported RADIUS authentication methods ("pap" or "chap"), in order of preference (default "pap"). .TP +.B radius_dae_port +Port for DAE RADIUS (Packet of Death/Disconnect, Change of Authorization) +requests (default: 3799). +.TP .B allow_duplicate_users Allow multiple logins with the same username. If false (the default), any prior session with the same username will be dropped when a new diff --git a/cli.c b/cli.c index 37682d6..d0f9a8a 100644 --- a/cli.c +++ b/cli.c @@ -2,7 +2,7 @@ // vim: sw=8 ts=8 char const *cvs_name = "$Name: $"; -char const *cvs_id_cli = "$Id: cli.c,v 1.62 2005-06-07 05:31:43 bodea Exp $"; +char const *cvs_id_cli = "$Id: cli.c,v 1.63 2005-06-28 14:48:17 bodea Exp $"; #include #include @@ -2316,17 +2316,6 @@ static int cmd_restart_bgp(struct cli_def *cli, char *command, char **argv, int #endif /* BGP*/ static int filt; -static int find_access_list(char const *name) -{ - int i; - - for (i = 0; i < MAXFILTER; i++) - if (!(*ip_filters[i].name && strcmp(ip_filters[i].name, name))) - return i; - - return -1; -} - static int access_list(struct cli_def *cli, char **argv, int argc, int add) { int extended; @@ -2377,7 +2366,7 @@ static int access_list(struct cli_def *cli, char **argv, int argc, int add) return CLI_OK; } - filt = find_access_list(argv[1]); + filt = find_filter(argv[1], strlen(argv[1])); if (add) { if (filt < 0) @@ -2970,7 +2959,7 @@ static int cmd_filter(struct cli_def *cli, char *command, char **argv, int argc) return CLI_OK; } - v = find_access_list(argv[i+1]); + v = find_filter(argv[i+1], strlen(argv[i+1])); if (v < 0 || !*ip_filters[v].name) { cli_error(cli, "Access-list %s not defined", argv[i+1]); @@ -3046,7 +3035,7 @@ static int cmd_show_access_list(struct cli_def *cli, char *command, char **argv, for (i = 0; i < argc; i++) { - int f = find_access_list(argv[i]); + int f = find_filter(argv[i], strlen(argv[i])); ip_filter_rulet *rules; if (f < 0 || !*ip_filters[f].name) diff --git a/cluster.c b/cluster.c index a8f34d0..ad41e46 100644 --- a/cluster.c +++ b/cluster.c @@ -1,6 +1,6 @@ // L2TPNS Clustering Stuff -char const *cvs_id_cluster = "$Id: cluster.c,v 1.43 2005-06-27 04:52:54 bodea Exp $"; +char const *cvs_id_cluster = "$Id: cluster.c,v 1.44 2005-06-28 14:48:19 bodea Exp $"; #include #include @@ -270,13 +270,8 @@ static int peer_send_message(in_addr_t peer, int type, int more, char *data, int return peer_send_data(peer, buf, (p-buf) ); } -// -// Forward a state changing packet to the master. -// -// The master just processes the payload as if it had -// received it off the tun device. -// -int master_forward_packet(char *data, int size, in_addr_t addr, int port) +// send a packet to the master +static int _forward_packet(char *data, int size, in_addr_t addr, int port, int type) { char buf[65536]; // Vast overkill. char *p = buf; @@ -287,13 +282,30 @@ int master_forward_packet(char *data, int size, in_addr_t addr, int port) LOG(4, 0, 0, "Forwarding packet from %s to master (size %d)\n", fmtaddr(addr, 0), size); STAT(c_forwarded); - add_type(&p, C_FORWARD, addr, (char *) &port, sizeof(port)); // ick. should be uint16_t + add_type(&p, type, addr, (char *) &port, sizeof(port)); // ick. should be uint16_t memcpy(p, data, size); p += size; return peer_send_data(config->cluster_master_address, buf, (p - buf)); } +// +// Forward a state changing packet to the master. +// +// The master just processes the payload as if it had +// received it off the tun device. +// +int master_forward_packet(char *data, int size, in_addr_t addr, int port) +{ + return _forward_packet(data, size, addr, port, C_FORWARD); +} + +// Forward a DAE RADIUS packet to the master. +int master_forward_dae_packet(char *data, int size, in_addr_t addr, int port) +{ + return _forward_packet(data, size, addr, port, C_FORWARD_DAE); +} + // // Forward a throttled packet to the master for handling. // @@ -1585,7 +1597,8 @@ int processcluster(char *data, int size, in_addr_t addr) p += sizeof(uint32_t); s -= sizeof(uint32_t); - switch (type) { + switch (type) + { case C_PING: // Update the peers table. return cluster_add_peer(addr, more, (pingt *) p, s); @@ -1595,24 +1608,36 @@ int processcluster(char *data, int size, in_addr_t addr) case C_LASTSEEN: // Catch up a slave (slave missed a packet). return cluster_catchup_slave(more, addr); - case C_FORWARD: { // Forwarded control packet. pass off to processudp. - struct sockaddr_in a; - a.sin_addr.s_addr = more; - - a.sin_port = *(int *) p; - s -= sizeof(int); - p += sizeof(int); + case C_FORWARD: // Forwarded control packet. pass off to processudp. + case C_FORWARD_DAE: // Forwarded DAE packet. pass off to processdae. + if (!config->cluster_iam_master) + { + LOG(0, 0, 0, "I'm not the master, but I got a C_FORWARD_%s from %s?\n", + type == C_FORWARD_DAE ? "_DAE" : "", fmtaddr(addr, 0)); - if (!config->cluster_iam_master) { // huh? - LOG(0, 0, 0, "I'm not the master, but I got a C_FORWARD from %s?\n", fmtaddr(addr, 0)); return -1; } + else + { + struct sockaddr_in a; + a.sin_addr.s_addr = more; + + a.sin_port = *(int *) p; + s -= sizeof(int); + p += sizeof(int); + + LOG(4, 0, 0, "Got a forwarded %spacket... (%s:%d)\n", + type == C_FORWARD_DAE ? "DAE " : "", fmtaddr(more, 0), a.sin_port); + + STAT(recv_forward); + if (type == C_FORWARD_DAE) + processdae(p, s, &a, sizeof(a)); + else + processudp(p, s, &a); + + return 0; + } - LOG(4, 0, 0, "Got a forwarded packet... (%s:%d)\n", fmtaddr(more, 0), a.sin_port); - STAT(recv_forward); - processudp(p, s, &a); - return 0; - } case C_THROTTLE: { // Receive a forwarded packet from a slave. if (!config->cluster_iam_master) { LOG(0, 0, 0, "I'm not the master, but I got a C_THROTTLE from %s?\n", fmtaddr(addr, 0)); diff --git a/cluster.h b/cluster.h index 00e8e9b..931c007 100644 --- a/cluster.h +++ b/cluster.h @@ -1,5 +1,5 @@ // L2TPNS Clustering Stuff -// $Id: cluster.h,v 1.12 2005-06-02 11:32:30 bodea Exp $ +// $Id: cluster.h,v 1.13 2005-06-28 14:48:19 bodea Exp $ #ifndef __CLUSTER_H__ #define __CLUSTER_H__ @@ -20,6 +20,7 @@ #define C_CTUNNEL 13 // Compressed tunnel structure. #define C_GARDEN 14 // Gardened packet #define C_MASTER 15 // Tell a slave the address of the master. +#define C_FORWARD_DAE 16 // A DAE packet for the master to handle #define HB_VERSION 5 // Protocol version number.. #define HB_MAX_SEQ (1<<30) // Maximum sequence number. (MUST BE A POWER OF 2!) @@ -75,6 +76,7 @@ int processcluster(char *buf, int size, in_addr_t addr); int cluster_send_session(int sid); int cluster_send_tunnel(int tid); int master_forward_packet(char *data, int size, in_addr_t addr, int port); +int master_forward_dae_packet(char *data, int size, in_addr_t addr, int port); int master_throttle_packet(int tid, char *data, int size); int master_garden_packet(sessionidt s, char *data, int size); void master_update_counts(void); diff --git a/constants.c b/constants.c index 0b9ef42..f9e3fa1 100644 --- a/constants.c +++ b/constants.c @@ -1,6 +1,6 @@ // L2TPNS: constants -char const *cvs_id_constants = "$Id: constants.c,v 1.5 2005-05-05 10:02:07 bodea Exp $"; +char const *cvs_id_constants = "$Id: constants.c,v 1.6 2005-06-28 14:48:20 bodea Exp $"; #include #include "constants.h" @@ -173,8 +173,17 @@ CONSTANT(radius_code, 0, // 9 0, // 10 "Access-Challenge", // 11 - "Status-Server (experimental)", // 12 - "Status-Client (experimental)" // 13 + "Status-Server", // 12 + "Status-Client", // 13 + 0, 0, 0, 0, 0, 0, // 14-19 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20-29 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 30-39 + "Disconnect-Request", // 40 + "Disconnect-ACK", // 41 + "Disconnect-NAK", // 42 + "CoA-Request", // 43 + "CoA-ACK", // 44 + "CoA-NAK" // 45 ) CONSTANT(l2tp_message_type, diff --git a/l2tpns.c b/l2tpns.c index 58246b3..50fde1d 100644 --- a/l2tpns.c +++ b/l2tpns.c @@ -4,7 +4,7 @@ // Copyright (c) 2002 FireBrick (Andrews & Arnold Ltd / Watchfront Ltd) - GPL licenced // vim: sw=8 ts=8 -char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.112 2005-06-24 07:05:04 bodea Exp $"; +char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.113 2005-06-28 14:48:20 bodea Exp $"; #include #include @@ -60,6 +60,7 @@ int tunfd = -1; // tun interface file handle. (network device) int udpfd = -1; // UDP file handle int controlfd = -1; // Control signal handle int clifd = -1; // Socket listening for CLI connections. +int daefd = -1; // Socket listening for DAE connections. int snoopfd = -1; // UDP file handle for sending out intercept data int *radfds = NULL; // RADIUS requests file handles int ifrfd = -1; // File descriptor for routing, etc @@ -114,6 +115,7 @@ config_descriptt config_values[] = { CONFIG("radius_interim", radius_interim, INT), CONFIG("radius_secret", radiussecret, STRING), CONFIG("radius_authtypes", radius_authtypes_s, STRING), + CONFIG("radius_dae_port", radius_dae_port, SHORT), CONFIG("allow_duplicate_users", allow_duplicate_users, BOOL), CONFIG("bind_address", bind_address, IPv4), CONFIG("peer_address", peer_address, IPv4), @@ -148,6 +150,7 @@ static char *plugin_functions[] = { "plugin_kill_session", "plugin_control", "plugin_radius_response", + "plugin_radius_reset", "plugin_become_master", "plugin_new_session_master", }; @@ -163,7 +166,7 @@ sessiont *session = NULL; // Array of session structures. sessionlocalt *sess_local = NULL; // Array of local per-session counters. radiust *radius = NULL; // Array of radius structures. ippoolt *ip_address_pool = NULL; // Array of dynamic IP addresses. -ip_filtert *ip_filters = NULL; // Array of named filters. +ip_filtert *ip_filters = NULL; // Array of named filters. static controlt *controlfree = 0; struct Tstats *_statistics = NULL; #ifdef RINGBUFFER @@ -578,7 +581,7 @@ static void inittun(void) } } -// set up UDP port +// set up UDP ports static void initudp(void) { int on = 1; @@ -600,7 +603,6 @@ static void initudp(void) LOG(0, 0, 0, "Error in UDP bind: %s\n", strerror(errno)); exit(1); } - snoopfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); // Control memset(&addr, 0, sizeof(addr)); @@ -613,6 +615,21 @@ static void initudp(void) LOG(0, 0, 0, "Error in control bind: %s\n", strerror(errno)); exit(1); } + + // Dynamic Authorization Extensions to RADIUS + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(config->radius_dae_port); + daefd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + setsockopt(daefd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + if (bind(daefd, (void *) &addr, sizeof(addr)) < 0) + { + LOG(0, 0, 0, "Error in DAE bind: %s\n", strerror(errno)); + exit(1); + } + + // Intercept + snoopfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); } // @@ -1415,7 +1432,7 @@ void throttle_session(sessionidt s, int rate_in, int rate_out) } // add/remove filters from session (-1 = no change) -static void filter_session(sessionidt s, int filter_in, int filter_out) +void filter_session(sessionidt s, int filter_in, int filter_out) { if (!session[s].opened) return; // No-one home. @@ -2959,8 +2976,8 @@ static int still_busy(void) # include "fake_epoll.h" #endif -// the base set of fds polled: control, cli, udp, tun, cluster -#define BASE_FDS 5 +// the base set of fds polled: cli, cluster, tun, udp, control, dae +#define BASE_FDS 6 // additional polled fds #ifdef BGP @@ -2984,8 +3001,8 @@ static void mainloop(void) exit(1); } - LOG(4, 0, 0, "Beginning of main loop. udpfd=%d, tunfd=%d, cluster_sockfd=%d, controlfd=%d\n", - udpfd, tunfd, cluster_sockfd, controlfd); + LOG(4, 0, 0, "Beginning of main loop. clifd=%d, cluster_sockfd=%d, tunfd=%d, udpfd=%d, controlfd=%d, daefd=%d\n", + clifd, cluster_sockfd, tunfd, udpfd, controlfd, daefd); /* setup our fds to poll for input */ { @@ -2995,25 +3012,29 @@ static void mainloop(void) e.events = EPOLLIN; i = 0; - d[i].type = FD_TYPE_CONTROL; - e.data.ptr = &d[i++]; - epoll_ctl(epollfd, EPOLL_CTL_ADD, controlfd, &e); - d[i].type = FD_TYPE_CLI; e.data.ptr = &d[i++]; epoll_ctl(epollfd, EPOLL_CTL_ADD, clifd, &e); - d[i].type = FD_TYPE_UDP; + d[i].type = FD_TYPE_CLUSTER; e.data.ptr = &d[i++]; - epoll_ctl(epollfd, EPOLL_CTL_ADD, udpfd, &e); + epoll_ctl(epollfd, EPOLL_CTL_ADD, cluster_sockfd, &e); d[i].type = FD_TYPE_TUN; e.data.ptr = &d[i++]; epoll_ctl(epollfd, EPOLL_CTL_ADD, tunfd, &e); - d[i].type = FD_TYPE_CLUSTER; + d[i].type = FD_TYPE_UDP; e.data.ptr = &d[i++]; - epoll_ctl(epollfd, EPOLL_CTL_ADD, cluster_sockfd, &e); + epoll_ctl(epollfd, EPOLL_CTL_ADD, udpfd, &e); + + d[i].type = FD_TYPE_CONTROL; + e.data.ptr = &d[i++]; + epoll_ctl(epollfd, EPOLL_CTL_ADD, controlfd, &e); + + d[i].type = FD_TYPE_DAE; + e.data.ptr = &d[i++]; + epoll_ctl(epollfd, EPOLL_CTL_ADD, daefd, &e); } #ifdef BGP @@ -3080,12 +3101,6 @@ static void mainloop(void) struct event_data *d = events[i].data.ptr; switch (d->type) { - case FD_TYPE_CONTROL: // nsctl commands - alen = sizeof(addr); - processcontrol(buf, recvfrom(controlfd, buf, sizeof(buf), MSG_WAITALL, (void *) &addr, &alen), &addr, alen); - n--; - break; - case FD_TYPE_CLI: // CLI connections { int cli; @@ -3104,9 +3119,21 @@ static void mainloop(void) } // these are handled below, with multiple interleaved reads - case FD_TYPE_UDP: udp_ready++; break; - case FD_TYPE_TUN: tun_ready++; break; case FD_TYPE_CLUSTER: cluster_ready++; break; + case FD_TYPE_TUN: tun_ready++; break; + case FD_TYPE_UDP: udp_ready++; break; + + case FD_TYPE_CONTROL: // nsctl commands + alen = sizeof(addr); + processcontrol(buf, recvfrom(controlfd, buf, sizeof(buf), MSG_WAITALL, (void *) &addr, &alen), &addr, alen); + n--; + break; + + case FD_TYPE_DAE: // DAE requests + alen = sizeof(addr); + processdae(buf, recvfrom(daefd, buf, sizeof(buf), MSG_WAITALL, (void *) &addr, &alen), &addr, alen); + n--; + break; case FD_TYPE_RADIUS: // RADIUS response s = recv(radfds[d->index], buf, sizeof(buf), 0); @@ -4186,6 +4213,9 @@ static void update_config() strcat(config->radius_authtypes_s, ", pap"); } + if (!config->radius_dae_port) + config->radius_dae_port = DAEPORT; + // re-initialise the random number source initrandom(config->random_device); @@ -5044,6 +5074,31 @@ static void unhide_value(uint8_t *value, size_t len, uint16_t type, uint8_t *vec } } +int find_filter(char const *name, size_t len) +{ + int free = -1; + int i; + + for (i = 0; i < MAXFILTER; i++) + { + if (!*ip_filters[i].name) + { + if (free < 0) + free = i; + + continue; + } + + if (strlen(ip_filters[i].name) != len) + continue; + + if (!strncmp(ip_filters[i].name, name, len)) + return i; + } + + return free; +} + static int ip_filter_port(ip_filter_portt *p, uint16_t port) { switch (p->op) diff --git a/l2tpns.h b/l2tpns.h index 6a359fd..a8e2d9a 100644 --- a/l2tpns.h +++ b/l2tpns.h @@ -1,5 +1,5 @@ // L2TPNS Global Stuff -// $Id: l2tpns.h,v 1.79 2005-06-24 07:05:04 bodea Exp $ +// $Id: l2tpns.h,v 1.80 2005-06-28 14:48:27 bodea Exp $ #ifndef __L2TPNS_H__ #define __L2TPNS_H__ @@ -76,6 +76,7 @@ #define ACCT_SHUT_TIME 600 // 1 minute for counters of shutdown sessions #define L2TPPORT 1701 // L2TP port #define RADPORT 1645 // old radius port... +#define DAEPORT 3799 // DAE port #define PKTARP 0x0806 // ARP packet type #define PKTIP 0x0800 // IPv4 packet type #define PKTIPV6 0x86DD // IPv6 packet type @@ -111,7 +112,13 @@ enum { AccessReject, AccountingRequest, AccountingResponse, - AccessChallenge = 11 + AccessChallenge = 11, + DisconnectRequest = 40, + DisconnectACK, + DisconnectNAK, + CoARequest, + CoAACK, + CoANAK }; // Types @@ -457,6 +464,8 @@ typedef struct uint16_t radiusport[MAXRADSERVER]; // radius base ports uint8_t numradiusservers; // radius server count + uint16_t radius_dae_port; // local port for radius dae + char radius_authtypes_s[32]; // list of valid authentication types (chap, pap) in order of preference int radius_authtypes; int radius_authprefer; @@ -611,6 +620,7 @@ void processrad(uint8_t *buf, int len, char socket_index); void radiusretry(uint16_t r); uint16_t radiusnew(sessionidt s); void radiusclear(uint16_t r, sessionidt s); +void processdae(uint8_t *buf, int len, struct sockaddr_in *addr, int alen); // l2tpns.c @@ -624,11 +634,13 @@ void increment_counter(uint32_t *counter, uint32_t *wrap, uint32_t delta); void random_data(uint8_t *buf, int len); void sessionkill(sessionidt s, char *reason); void sessionshutdown(sessionidt s, char *reason, int result, int error); +void filter_session(sessionidt s, int filter_in, int filter_out); void send_garp(in_addr_t ip); void tunnelsend(uint8_t *buf, uint16_t l, tunnelidt t); void sendipcp(tunnelidt t, sessionidt s); void processudp(uint8_t *buf, int len, struct sockaddr_in *addr); void snoop_send_packet(char *packet, uint16_t size, in_addr_t destination, uint16_t port); +int find_filter(char const *name, size_t len); int ip_filter(uint8_t *buf, int len, uint8_t filter); int cmd_show_ipcache(struct cli_def *cli, char *command, char **argv, int argc); int cmd_show_hist_idle(struct cli_def *cli, char *command, char **argv, int argc); @@ -696,11 +708,12 @@ extern int epollfd; struct event_data { enum { - FD_TYPE_CONTROL, FD_TYPE_CLI, - FD_TYPE_UDP, - FD_TYPE_TUN, FD_TYPE_CLUSTER, + FD_TYPE_TUN, + FD_TYPE_UDP, + FD_TYPE_CONTROL, + FD_TYPE_DAE, FD_TYPE_RADIUS, FD_TYPE_BGP, } type; diff --git a/l2tpns.spec b/l2tpns.spec index 0557ab7..6b760a1 100644 --- a/l2tpns.spec +++ b/l2tpns.spec @@ -43,5 +43,5 @@ rm -rf %{buildroot} %attr(644,root,root) /usr/share/man/man[58]/* %changelog -* Mon Jun 27 2005 Brendan O'Dea 2.1.2-1 +* Wed Jun 29 2005 Brendan O'Dea 2.1.2-1 - 2.1.2 release, see /usr/share/doc/l2tpns-2.1.2/Changes diff --git a/plugin.h b/plugin.h index dcae48d..6619ad3 100644 --- a/plugin.h +++ b/plugin.h @@ -1,7 +1,7 @@ #ifndef __PLUGIN_H__ #define __PLUGIN_H__ -#define PLUGIN_API_VERSION 5 +#define PLUGIN_API_VERSION 6 #define MAX_PLUGIN_TYPES 30 enum @@ -15,6 +15,7 @@ enum PLUGIN_KILL_SESSION, PLUGIN_CONTROL, PLUGIN_RADIUS_RESPONSE, + PLUGIN_RADIUS_RESET, PLUGIN_BECOME_MASTER, PLUGIN_NEW_SESSION_MASTER, }; @@ -111,4 +112,10 @@ struct param_radius_response char *value; }; +struct param_radius_reset +{ + tunnelt *t; + sessiont *s; +}; + #endif /* __PLUGIN_H__ */ diff --git a/radius.c b/radius.c index f0500b8..c2474e3 100644 --- a/radius.c +++ b/radius.c @@ -1,6 +1,6 @@ // L2TPNS Radius Stuff -char const *cvs_id_radius = "$Id: radius.c,v 1.33 2005-06-04 15:42:36 bodea Exp $"; +char const *cvs_id_radius = "$Id: radius.c,v 1.34 2005-06-28 14:48:28 bodea Exp $"; #include #include @@ -12,11 +12,13 @@ char const *cvs_id_radius = "$Id: radius.c,v 1.33 2005-06-04 15:42:36 bodea Exp #include #include #include +#include #include "md5.h" #include "constants.h" #include "l2tpns.h" #include "plugin.h" #include "util.h" +#include "cluster.h" extern radiust *radius; extern sessiont *session; @@ -51,7 +53,7 @@ static uint16_t get_free_radius() int count; static uint32_t next_radius_id = 0; - for (count = MAXRADIUS; count > 0 ; --count) + for (count = MAXRADIUS; count > 0; --count) { ++next_radius_id; // Find the next ID to check. if (next_radius_id >= MAXRADIUS) @@ -296,7 +298,7 @@ void radiussend(uint16_t r, uint8_t state) { *p = 26; // vendor-specific *(uint32_t *) (p + 2) = htonl(9); // Cisco - p[6] = 1; // Cisco-Avpair + p[6] = 1; // Cisco-AVPair p[7] = 2 + sprintf(p + 8, "intercept=%s:%d", fmtaddr(session[s].snoop_ip, 0), session[s].snoop_port); @@ -377,6 +379,47 @@ void radiussend(uint16_t r, uint8_t state) sendto(radfds[r & RADIUS_MASK], b, p - b, 0, (void *) &addr, sizeof(addr)); } +static void handle_avpair(sessionidt s, uint8_t *avp, int len) +{ + char *key = avp; + char *value = memchr(avp, '=', len); + char tmp[2048] = ""; + + if (value) + { + *value++ = 0; + len -= value - key; + } + else + { + value = tmp; + len = 0; + } + + // strip quotes + if (len > 2 && (*value == '"' || *value == '\'') && value[len - 1] == *value) + { + value++; + len--; + value[len - 1] = 0; + } + // copy and null terminate + else if (len < sizeof(tmp) - 1) + { + memcpy(tmp, value, len); + tmp[len] = 0; + value = tmp; + } + else + return; + + // Run hooks + { + struct param_radius_response p = { &tunnel[session[s].tunnel], &session[s], key, value }; + run_plugins(PLUGIN_RADIUS_RESPONSE, &p); + } +} + // process RADIUS response void processrad(uint8_t *buf, int len, char socket_index) { @@ -577,37 +620,36 @@ void processrad(uint8_t *buf, int len, char socket_index) char *filter = p + 2; int l = p[1] - 2; char *suffix; - uint8_t *f = 0; - int i; + int f; + uint8_t *fp = 0; LOG(3, s, session[s].tunnel, " Radius reply contains Filter-Id \"%.*s\"\n", l, filter); if ((suffix = memchr(filter, '.', l))) { int b = suffix - filter; if (l - b == 3 && !memcmp("in", suffix+1, 2)) - f = &session[s].filter_in; + fp = &session[s].filter_in; else if (l - b == 4 && !memcmp("out", suffix+1, 3)) - f = &session[s].filter_out; + fp = &session[s].filter_out; l = b; } - if (!f) + if (!fp) { LOG(3, s, session[s].tunnel, " Invalid filter\n"); continue; } - for (*f = 0, i = 0; !*f && i < MAXFILTER; i++) - if (strlen(ip_filters[i].name) == l && - !strncmp(ip_filters[i].name, filter, l)) - *f = i + 1; - - if (*f) - ip_filters[*f - 1].used++; - else + if ((f = find_filter(filter, l)) < 0 || !*ip_filters[f].name) + { LOG(3, s, session[s].tunnel, " Unknown filter\n"); - + } + else + { + *fp = f + 1; + ip_filters[f].used++; + } } else if (*p == 26 && p[1] >= 7) { @@ -615,7 +657,6 @@ void processrad(uint8_t *buf, int len, char socket_index) int vendor = ntohl(*(int *)(p + 2)); char attrib = *(p + 6); int attrib_length = *(p + 7) - 2; - char *avpair, *value, *key, *newp; LOG(3, s, session[s].tunnel, " Radius reply contains Vendor-Specific. Vendor=%d Attrib=%d Length=%d\n", vendor, attrib, attrib_length); if (vendor != 9 || attrib != 1) @@ -624,36 +665,13 @@ void processrad(uint8_t *buf, int len, char socket_index) continue; } - if (attrib_length < 0) continue; - - avpair = key = calloc(attrib_length + 1, 1); - memcpy(avpair, p + 8, attrib_length); - LOG(3, s, session[s].tunnel, " Cisco-Avpair value: %s\n", avpair); - do { - value = strchr(key, '='); - if (!value) break; - *value++ = 0; - - // Trim quotes off reply string - if (*value == '\'' || *value == '\"') - { - char *x; - value++; - x = value + strlen(value) - 1; - if (*x == '\'' || *x == '\"') - *x = 0; - } + if (attrib_length > 0) + { + LOG(3, s, session[s].tunnel, " Cisco-AVPair value: %.*s\n", + attrib_length, p + 8); - // Run hooks - newp = strchr(value, ','); - if (newp) *newp++ = 0; - { - struct param_radius_response p = { &tunnel[session[s].tunnel], &session[s], key, value }; - run_plugins(PLUGIN_RADIUS_RESPONSE, &p); - } - key = newp; - } while (newp); - free(avpair); + handle_avpair(s, p + 8, attrib_length); + } } else if (*p == 99) { @@ -756,3 +774,319 @@ void radiusretry(uint16_t r) break; } } + +extern int daefd; + +void processdae(uint8_t *buf, int len, struct sockaddr_in *addr, int alen) +{ + int i, r_code, r_id, length, attribute_length; + uint8_t vector[16], hash[16], *packet, attribute; + MD5_CTX ctx; + char username[MAXUSER] = ""; + in_addr_t nas = 0; + in_addr_t ip = 0; + uint32_t port = 0; + uint32_t error = 0; + sessionidt s = 0; + tunnelidt t; + int fin = -1; + int fout = -1; + uint8_t *avpair[64]; + int avpair_len[sizeof(avpair)/sizeof(*avpair)]; + int avp = 0; + int auth_only = 0; + uint8_t *p; + + LOG(3, 0, 0, "DAE request from %s\n", fmtaddr(addr->sin_addr.s_addr, 0)); + + // check if DAE is from RADIUS server + for (i = 0; i < config->numradiusservers; i++) + if (config->radiusserver[i] == addr -> sin_addr.s_addr) + break; + + if (i >= config->numradiusservers) + { + LOG(1, 0, 0, "Unknown DAE client %s\n", fmtaddr(addr->sin_addr.s_addr, 0)); + return; + } + + LOG_HEX(5, "DAE Request", buf, len); + + if (len < 20 || len < ntohs(*(uint16_t *) (buf + 2))) + { + LOG(1, 0, 0, "Duff DAE request length %d\n", len); + return; + } + + r_code = buf[0]; // request type + r_id = buf[1]; // radius indentifier. + + if (r_code != DisconnectRequest && r_code != CoARequest) + { + LOG(1, 0, 0, "Unrecognised DAE request %s\n", radius_code(r_code)); + return; + } + + if (!config->cluster_iam_master) + { + master_forward_dae_packet(buf, len, addr->sin_addr.s_addr, addr->sin_port); + return; + } + + len = ntohs(*(uint16_t *) (buf + 2)); + + LOG(3, 0, 0, "Received DAE %s, id %d\n", radius_code(r_code), r_id); + + // check authenticator + memcpy(vector, buf + 4, 16); + memset(buf + 4, 0, 16); + + i = strlen(config->radiussecret); + if (i > 16) i = 16; + + MD5Init(&ctx); + MD5Update(&ctx, buf, len); + MD5Update(&ctx, buf, config->radiussecret, i); + MD5Final(hash, &ctx); + if (memcmp(hash, vector, 16) != 0) + { + LOG(1, 0, 0, "Incorrect vector in DAE request (wrong secret in radius config?)\n"); + return; + } + + // unpack attributes + packet = buf + 20; + length = len - 20; + + while (length > 0) + { + attribute = *packet++; + attribute_length = *packet++; + if (attribute_length < 2) + break; + + length -= attribute_length; + attribute_length -= 2; + switch (attribute) + { + case 1: /* username */ + len = attribute_length < MAXUSER ? attribute_length : MAXUSER - 1; + memcpy(username, packet, len); + username[len] = 0; + LOG(4, 0, 0, " Received DAE User-Name: %s\n", username); + break; + + case 4: /* nas ip address */ + nas = *(uint32_t *) packet; // net order + if (nas != config->bind_address) + error = 403; // NAS identification mismatch + + LOG(4, 0, 0, " Received DAE NAS-IP-Address: %s\n", fmtaddr(nas, 0)); + break; + + case 5: /* nas port */ + port = ntohl(*(uint32_t *) packet); + if (port < 1 || port > MAXSESSION) + error = 404; + + LOG(4, 0, 0, " Received DAE NAS-Port: %u\n", port); + break; + + case 6: /* service type */ + { + uint32_t service_type = ntohl(*(uint32_t *) packet); + auth_only = service_type == 8; // Authenticate only + + LOG(4, 0, 0, " Received DAE Service-Type: %u\n", service_type); + } + break; + + case 8: /* ip address */ + ip = *(uint32_t *) packet; // net order + LOG(4, 0, 0, " Received DAE Framed-IP-Address: %s\n", fmtaddr(ip, 0)); + break; + + case 11: /* filter id */ + LOG(4, 0, 0, " Received DAE Filter-Id: %.*s\n", attribute_length, packet); + if (!(p = memchr(packet, '.', attribute_length))) + { + error = 404; // invalid request + break; + } + + len = p - packet; + i = find_filter(packet, len); + if (i < 0 || !*ip_filters[i].name) + { + error = 404; + break; + } + + if (!memcmp(p, ".in", attribute_length - len)) + fin = i + 1; + else if (!memcmp(p, ".out", attribute_length - len)) + fout = i + 1; + else + error = 404; + + break; + + case 26: /* vendor specific */ + if (attribute_length >= 6 + && ntohl(*(uint32_t *) packet) == 9 // Cisco + && *(packet + 4) == 1 // Cisco-AVPair + && *(packet + 5) >= 2) // length + { + int len = *(packet + 5) - 2; + uint8_t *a = packet + 6; + + LOG(4, 0, 0, " Received DAE Cisco-AVPair: %.*s\n", len, a); + if (avp < sizeof(avpair)/sizeof(*avpair) - 1) + { + avpair[avp] = a; + avpair_len[avp++] = len; + } + } + break; + } + + packet += attribute_length; + } + + if (!error && auth_only) + { + if (fin != -1 || fout != -1 || avp) + error = 401; // unsupported attribute + else + error = 405; // unsupported service + } + + if (!error && !(port || ip || *username)) + error = 402; // missing attribute + + // exact match for SID if given + if (!error && port) + { + s = port; + if (!session[s].opened) + error = 503; // not found + } + + if (!error && ip) + { + // find/check session by IP + i = sessionbyip(ip); + if (!i || (s && s != i)) // not found or mismatching port + error = 503; + else + s = i; + } + + if (!error && *username) + { + if (s) + { + if (strcmp(session[s].user, username)) + error = 503; + } + else if (!(s = sessionbyuser(username))) + error = 503; + } + + t = session[s].tunnel; + + switch (r_code) + { + case DisconnectRequest: // Packet of Disconnect/Death + if (error) + { + r_code = DisconnectNAK; + break; + } + + LOG(3, s, t, " DAE Disconnect %d (%s)\n", s, session[s].user); + r_code = DisconnectACK; + + sessionshutdown(s, "Requested by PoD", 3, 0); // disconnect session + break; + + case CoARequest: // Change of Authorization + if (error) + { + r_code = CoANAK; + break; + } + + LOG(3, s, t, " DAE Change %d (%s)\n", s, session[s].user); + r_code = CoAACK; + + // reset + { + struct param_radius_reset p = { &tunnel[session[s].tunnel], &session[s] }; + run_plugins(PLUGIN_RADIUS_RESET, &p); + } + + // apply filters + if (fin != -1 || fout != -1) + { + if (fin == -1) + fin = 0; + else + LOG(3, s, t, " Filter in %d (%s)\n", fin, ip_filters[fin - 1].name); + + if (fout == -1) + fout = 0; + else + LOG(3, s, t, " Filter out %d (%s)\n", fout, ip_filters[fout - 1].name); + + filter_session(s, fin, fout); + } + + // process cisco av-pair(s) + for (i = 0; i < avp; i++) + { + LOG(3, s, t, " Cisco-AVPair: %.*s\n", avpair_len[i], avpair[i]); + handle_avpair(s, avpair[i], avpair_len[i]); + } + + cluster_send_session(s); + break; + } + + // send response + packet = buf; + *packet++ = r_code; + *packet++ = r_id; + packet += 2; + memset(packet, 0, 16); + packet += 16; + len = 20; + + // add attributes + if (error) + { + // add error cause + *packet++ = 101; + *packet++ = 6; + *(uint32_t *) packet = htonl(error); + len += 6; + } + + *((uint16_t *)(buf + 2)) = htons(len); + + // make vector + i = strlen(config->radiussecret); + if (i > 16) i = 16; + + MD5Init(&ctx); + MD5Update(&ctx, buf, len); + MD5Update(&ctx, config->radiussecret, i); + MD5Final(hash, &ctx); + memcpy(buf + 4, hash, 16); + + LOG(3, 0, 0, "Sending DAE %s, id=%d\n", radius_code(r_code), r_id); + + // send DAE response + if (sendto(daefd, buf, len, MSG_DONTWAIT | MSG_NOSIGNAL, (struct sockaddr *) addr, alen) < 0) + LOG(0, 0, 0, "Error sending DAE response packet: %s\n", strerror(errno)); +} diff --git a/sessionctl.c b/sessionctl.c index 7cb0316..0aae1c9 100644 --- a/sessionctl.c +++ b/sessionctl.c @@ -5,7 +5,7 @@ /* session control */ -char const *cvs_id = "$Id: sessionctl.c,v 1.2 2005-05-10 06:48:16 bodea Exp $"; +char const *cvs_id = "$Id: sessionctl.c,v 1.3 2005-06-28 14:48:28 bodea Exp $"; int plugin_api_version = PLUGIN_API_VERSION; static struct pluginfuncs *p = 0; @@ -16,15 +16,6 @@ char *plugin_control_help[] = { 0 }; -int plugin_init(struct pluginfuncs *funcs) -{ - if (!funcs) - return 0; - - p = funcs; - return 1; -} - int plugin_control(struct param_control *data) { sessionidt session; @@ -76,3 +67,8 @@ int plugin_control(struct param_control *data) return PLUGIN_RET_STOP; } + +int plugin_init(struct pluginfuncs *funcs) +{ + return ((p = funcs)) ? 1 : 0; +} diff --git a/snoopctl.c b/snoopctl.c index bc5fa28..f6e8535 100644 --- a/snoopctl.c +++ b/snoopctl.c @@ -5,7 +5,7 @@ /* snoop control */ -char const *cvs_id = "$Id: snoopctl.c,v 1.4 2004-12-16 08:49:53 bodea Exp $"; +char const *cvs_id = "$Id: snoopctl.c,v 1.5 2005-06-28 14:48:28 bodea Exp $"; int plugin_api_version = PLUGIN_API_VERSION; static struct pluginfuncs *p = 0; @@ -16,15 +16,6 @@ char *plugin_control_help[] = { 0 }; -int plugin_init(struct pluginfuncs *funcs) -{ - if (!funcs) - return 0; - - p = funcs; - return 1; -} - int plugin_control(struct param_control *data) { sessionidt session; @@ -124,3 +115,15 @@ int plugin_control(struct param_control *data) return PLUGIN_RET_STOP; } + +int plugin_radius_reset(struct param_radius_reset *data) +{ + data->s->snoop_ip = 0; + data->s->snoop_port = 0; + return PLUGIN_RET_OK; +} + +int plugin_init(struct pluginfuncs *funcs) +{ + return ((p = funcs)) ? 1 : 0; +} diff --git a/throttlectl.c b/throttlectl.c index 3aeb963..7aca35e 100644 --- a/throttlectl.c +++ b/throttlectl.c @@ -5,7 +5,7 @@ /* throttle control */ -char const *cvs_id = "$Id: throttlectl.c,v 1.6 2004-12-01 04:44:29 bodea Exp $"; +char const *cvs_id = "$Id: throttlectl.c,v 1.7 2005-06-28 14:48:28 bodea Exp $"; int plugin_api_version = PLUGIN_API_VERSION; static struct pluginfuncs *p = 0; @@ -16,15 +16,6 @@ char *plugin_control_help[] = { 0 }; -int plugin_init(struct pluginfuncs *funcs) -{ - if (!funcs) - return 0; - - p = funcs; - return 1; -} - int plugin_control(struct param_control *data) { sessionidt session; @@ -137,3 +128,14 @@ int plugin_control(struct param_control *data) return PLUGIN_RET_STOP; } + +int plugin_radius_reset(struct param_radius_reset *data) +{ + p->throttle(p->get_id_by_session(data->s), 0, 0); + return PLUGIN_RET_OK; +} + +int plugin_init(struct pluginfuncs *funcs) +{ + return ((p = funcs)) ? 1 : 0; +} diff --git a/util.c b/util.c index 0ba92ec..e2941c8 100644 --- a/util.c +++ b/util.c @@ -1,6 +1,6 @@ /* Misc util functions */ -char const *cvs_id_util = "$Id: util.c,v 1.11 2005-06-04 15:42:36 bodea Exp $"; +char const *cvs_id_util = "$Id: util.c,v 1.12 2005-06-28 14:48:28 bodea Exp $"; #include #include @@ -40,7 +40,7 @@ void *shared_malloc(unsigned int size) } extern int forked; -extern int udpfd, controlfd, tunfd, snoopfd, ifrfd, ifr6fd, rand_fd, cluster_sockfd; +extern int cluster_sockfd, tunfd, udpfd, controlfd, daefd, snoopfd, ifrfd, ifr6fd, rand_fd; extern int *radfds; pid_t fork_and_close() @@ -73,15 +73,16 @@ pid_t fork_and_close() signal(SIGTERM, SIG_DFL); // Close sockets + if (clifd != -1) close(clifd); + if (cluster_sockfd != -1) close(cluster_sockfd); if (tunfd != -1) close(tunfd); if (udpfd != -1) close(udpfd); if (controlfd != -1) close(controlfd); + if (daefd != -1) close(daefd); if (snoopfd != -1) close(snoopfd); if (ifrfd != -1) close(ifrfd); if (ifr6fd != -1) close(ifr6fd); if (rand_fd != -1) close(rand_fd); - if (cluster_sockfd != -1) close(cluster_sockfd); - if (clifd != -1) close(clifd); if (epollfd != -1) close(epollfd); for (i = 0; radfds && i < RADIUS_FDS; i++)