-* Mon Jun 27 2005 Brendan O'Dea <bod@optus.net> 2.1.2
+* Wed Jun 29 2005 Brendan O'Dea <bod@c47.org> 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 <bod@optusnet.com.au> 2.1.1
- Add missing newline to backtrace macro.
(<B>pap</B> or <B>chap</B>), in order of preference (default <B>pap</B>).
</LI>
+<LI><B>radius_dae_port</B> (short)<BR>
+Port for DAE RADIUS (Packet of Death/Disconnect, Change of Authorization)
+requests (default: <B>3799</B>).
+</LI>
+
<LI><B>allow_duplicate_users</B> (boolean)</BR>
Allow multiple logins with the same username. If false (the default),
any prior session with the same username will be dropped when a new
.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
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
// 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 <stdio.h>
#include <stdarg.h>
#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;
return CLI_OK;
}
- filt = find_access_list(argv[1]);
+ filt = find_filter(argv[1], strlen(argv[1]));
if (add)
{
if (filt < 0)
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]);
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)
// 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 <stdio.h>
#include <stdlib.h>
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;
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.
//
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);
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));
// 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__
#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!)
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);
// 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 <stdio.h>
#include "constants.h"
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,
// 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 <arpa/inet.h>
#include <assert.h>
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
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),
"plugin_kill_session",
"plugin_control",
"plugin_radius_response",
+ "plugin_radius_reset",
"plugin_become_master",
"plugin_new_session_master",
};
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
}
}
-// set up UDP port
+// set up UDP ports
static void initudp(void)
{
int on = 1;
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));
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);
}
//
}
// 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.
# 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
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 */
{
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
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;
}
// 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);
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);
}
}
+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)
// 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__
#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
AccessReject,
AccountingRequest,
AccountingResponse,
- AccessChallenge = 11
+ AccessChallenge = 11,
+ DisconnectRequest = 40,
+ DisconnectACK,
+ DisconnectNAK,
+ CoARequest,
+ CoAACK,
+ CoANAK
};
// Types
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;
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
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);
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;
%attr(644,root,root) /usr/share/man/man[58]/*
%changelog
-* Mon Jun 27 2005 Brendan O'Dea <bod@optus.net> 2.1.2-1
+* Wed Jun 29 2005 Brendan O'Dea <bod@c47.org> 2.1.2-1
- 2.1.2 release, see /usr/share/doc/l2tpns-2.1.2/Changes
#ifndef __PLUGIN_H__
#define __PLUGIN_H__
-#define PLUGIN_API_VERSION 5
+#define PLUGIN_API_VERSION 6
#define MAX_PLUGIN_TYPES 30
enum
PLUGIN_KILL_SESSION,
PLUGIN_CONTROL,
PLUGIN_RADIUS_RESPONSE,
+ PLUGIN_RADIUS_RESET,
PLUGIN_BECOME_MASTER,
PLUGIN_NEW_SESSION_MASTER,
};
char *value;
};
+struct param_radius_reset
+{
+ tunnelt *t;
+ sessiont *s;
+};
+
#endif /* __PLUGIN_H__ */
// 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 <time.h>
#include <stdio.h>
#include <arpa/inet.h>
#include <ctype.h>
#include <netinet/in.h>
+#include <errno.h>
#include "md5.h"
#include "constants.h"
#include "l2tpns.h"
#include "plugin.h"
#include "util.h"
+#include "cluster.h"
extern radiust *radius;
extern sessiont *session;
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)
{
*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);
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)
{
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)
{
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)
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)
{
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));
+}
/* 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;
0
};
-int plugin_init(struct pluginfuncs *funcs)
-{
- if (!funcs)
- return 0;
-
- p = funcs;
- return 1;
-}
-
int plugin_control(struct param_control *data)
{
sessionidt session;
return PLUGIN_RET_STOP;
}
+
+int plugin_init(struct pluginfuncs *funcs)
+{
+ return ((p = funcs)) ? 1 : 0;
+}
/* 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;
0
};
-int plugin_init(struct pluginfuncs *funcs)
-{
- if (!funcs)
- return 0;
-
- p = funcs;
- return 1;
-}
-
int plugin_control(struct param_control *data)
{
sessionidt session;
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;
+}
/* 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;
0
};
-int plugin_init(struct pluginfuncs *funcs)
-{
- if (!funcs)
- return 0;
-
- p = funcs;
- return 1;
-}
-
int plugin_control(struct param_control *data)
{
sessionidt session;
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;
+}
/* 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 <unistd.h>
#include <errno.h>
}
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()
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++)