add DAE support (PoD/CoA) from Vladislav Bjelic

This commit is contained in:
bodea 2005-06-28 14:48:17 +00:00
parent 659ed315c2
commit 4f253feef0
16 changed files with 604 additions and 158 deletions

View file

@ -1,9 +1,10 @@
* 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.

View file

@ -229,6 +229,11 @@ A comma separated list of supported RADIUS authentication methods
(<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

View file

@ -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

19
cli.c
View file

@ -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 <stdio.h>
#include <stdarg.h>
@ -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)

View file

@ -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 <stdio.h>
#include <stdlib.h>
@ -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,7 +1608,17 @@ 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.
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));
return -1;
}
else
{
struct sockaddr_in a;
a.sin_addr.s_addr = more;
@ -1603,16 +1626,18 @@ int processcluster(char *data, int size, in_addr_t addr)
s -= sizeof(int);
p += sizeof(int);
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;
}
LOG(4, 0, 0, "Got a forwarded %spacket... (%s:%d)\n",
type == C_FORWARD_DAE ? "DAE " : "", fmtaddr(more, 0), a.sin_port);
LOG(4, 0, 0, "Got a forwarded packet... (%s:%d)\n", 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;
}
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));

View file

@ -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);

View file

@ -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 <stdio.h>
#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,

103
l2tpns.c
View file

@ -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 <arpa/inet.h>
#include <assert.h>
@ -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",
};
@ -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)

View file

@ -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;

View file

@ -43,5 +43,5 @@ rm -rf %{buildroot}
%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

View file

@ -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__ */

422
radius.c
View file

@ -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 <time.h>
#include <stdio.h>
@ -12,11 +12,13 @@ char const *cvs_id_radius = "$Id: radius.c,v 1.33 2005/06/04 15:42:36 bodea Exp
#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;
@ -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 == '\"')
if (attrib_length > 0)
{
char *x;
value++;
x = value + strlen(value) - 1;
if (*x == '\'' || *x == '\"')
*x = 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);
handle_avpair(s, p + 8, attrib_length);
}
key = newp;
} while (newp);
free(avpair);
}
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));
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}

9
util.c
View file

@ -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 <unistd.h>
#include <errno.h>
@ -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++)