add Multilink support from Khaled Al Hamwi

This commit is contained in:
bodea 2006-04-27 09:53:49 +00:00
parent 63492a7d1d
commit 5e631c6978
13 changed files with 1021 additions and 97 deletions

View file

@ -1,6 +1,7 @@
* Tue Apr 25 2006 Brendan O'Dea <bod@optus.net> 2.1.19
* Thu Apr 27 2006 Brendan O'Dea <bod@optus.net> 2.2.0
- Only poll clifd if successfully bound.
- Add "Practical VPNs" document from Liran Tal as Docs/vpn .
- Add Multilink support from Khaled Al Hamwi.
* Tue Apr 18 2006 Brendan O'Dea <bod@optus.net> 2.1.18
- Don't shutdown on TerminateReq, wait for CDN.

View file

@ -2,7 +2,7 @@
.de Id
.ds Dt \\$4 \\$5
..
.Id $Id: startup-config.5,v 1.15 2005/09/16 05:04:31 bodea Exp $
.Id $Id: startup-config.5,v 1.16 2006/04/27 09:53:50 bodea Exp $
.TH STARTUP-CONFIG 5 "\*(Dt" L2TPNS "File Formats and Conventions"
.SH NAME
startup\-config \- configuration file for l2tpns
@ -123,6 +123,9 @@ Allow multiple logins with the same username. If false (the default),
any prior session with the same username will be dropped when a new
session is established.
.TP
.B guest_account
Allow multiple logins matching this specific username.
.TP
.B bind_address
When the tun interface is created, it is assigned the address
specified here. If no address is given, 1.1.1.1 is used. Packets

1
THANKS
View file

@ -26,3 +26,4 @@ Jon Morby <jon@fido.net>
Paul Martin <pm@zetnet.net>
Jonathan Yarden <jyarden@bluegrass.net>
Patrick Cole <z@amused.net>
Khaled Al Hamwi <kh.alhamwi@gmail.com>

23
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.71 2005/12/06 09:43:42 bodea Exp $";
char const *cvs_id_cli = "$Id: cli.c,v 1.72 2006/04/27 09:53:49 bodea Exp $";
#include <stdio.h>
#include <stddef.h>
@ -36,6 +36,7 @@ char const *cvs_id_cli = "$Id: cli.c,v 1.71 2005/12/06 09:43:42 bodea Exp $";
#endif
extern tunnelt *tunnel;
extern bundlet *bundle;
extern sessiont *session;
extern radiust *radius;
extern ippoolt *ip_address_pool;
@ -426,6 +427,14 @@ static int cmd_show_session(struct cli_def *cli, char *command, char **argv, int
cli_print(cli, "\tOpened:\t\t%u seconds", session[s].opened ? abs(time_now - session[s].opened) : 0);
cli_print(cli, "\tIdle time:\t%u seconds", session[s].last_packet ? abs(time_now - session[s].last_packet) : 0);
cli_print(cli, "\tBytes In/Out:\t%u/%u", session[s].cout, session[s].cin);
if (session[s].timeout)
{
cli_print(cli, "\tRemaing time:\t%u",
(session[s].bundle && bundle[session[s].bundle].num_of_links > 1)
? (unsigned) (session[s].timeout - bundle[session[s].bundle].online_time)
: (unsigned) (session[s].timeout - (time_now - session[s].opened)));
}
cli_print(cli, "\tPkts In/Out:\t%u/%u", session[s].pout, session[s].pin);
cli_print(cli, "\tMRU:\t\t%d", session[s].mru);
cli_print(cli, "\tRx Speed:\t%u", session[s].rx_connect_speed);
@ -492,7 +501,7 @@ static int cmd_show_session(struct cli_def *cli, char *command, char **argv, int
}
// Show Summary
cli_print(cli, "%5s %4s %-32s %-15s %s %s %s %s %10s %10s %10s %4s %-15s %s",
cli_print(cli, "%5s %4s %-32s %-15s %s %s %s %s %10s %10s %10s %4s %10s %-15s %s",
"SID",
"TID",
"Username",
@ -505,13 +514,20 @@ static int cmd_show_session(struct cli_def *cli, char *command, char **argv, int
"downloaded",
"uploaded",
"idle",
"Rem.Time",
"LAC",
"CLI");
for (i = 1; i < MAXSESSION; i++)
{
uint32_t rem_time;
if (!session[i].opened) continue;
cli_print(cli, "%5d %4d %-32s %-15s %s %s %s %s %10u %10lu %10lu %4u %-15s %s",
if (session[i].bundle && bundle[session[i].bundle].num_of_links > 1)
rem_time = session[i].timeout ? (session[i].timeout - bundle[session[i].bundle].online_time) : 0;
else
rem_time = session[i].timeout ? (session[i].timeout - (time_now-session[i].opened)) : 0;
cli_print(cli, "%5d %4d %-32s %-15s %s %s %s %s %10u %10lu %10lu %4u %10lu %-15s %s",
i,
session[i].tunnel,
session[i].user[0] ? session[i].user : "*",
@ -524,6 +540,7 @@ static int cmd_show_session(struct cli_def *cli, char *command, char **argv, int
(unsigned long)session[i].cout,
(unsigned long)session[i].cin,
abs(time_now - (session[i].last_packet ? session[i].last_packet : time_now)),
(unsigned long)(rem_time),
fmtaddr(htonl(tunnel[ session[i].tunnel ].ip), 1),
session[i].calling[0] ? session[i].calling : "*");
}

160
cluster.c
View file

@ -1,6 +1,6 @@
// L2TPNS Clustering Stuff
char const *cvs_id_cluster = "$Id: cluster.c,v 1.50 2006/04/05 02:13:48 bodea Exp $";
char const *cvs_id_cluster = "$Id: cluster.c,v 1.51 2006/04/27 09:53:49 bodea Exp $";
#include <stdio.h>
#include <stdlib.h>
@ -42,6 +42,7 @@ extern int cluster_sockfd; // The filedescriptor for the cluster communications
in_addr_t my_address = 0; // The network address of my ethernet port.
static int walk_session_number = 0; // The next session to send when doing the slow table walk.
static int walk_bundle_number = 0; // The next bundle to send when doing the slow table walk.
static int walk_tunnel_number = 0; // The next tunnel to send when doing the slow table walk.
int forked = 0; // Sanity check: CLI must not diddle with heartbeat table
@ -85,6 +86,7 @@ int cluster_init()
int opt;
config->cluster_undefined_sessions = MAXSESSION-1;
config->cluster_undefined_bundles = MAXBUNDLE-1;
config->cluster_undefined_tunnels = MAXTUNNEL-1;
if (!config->cluster_address)
@ -227,7 +229,7 @@ static void cluster_uptodate(void)
if (config->cluster_iam_uptodate)
return;
if (config->cluster_undefined_sessions || config->cluster_undefined_tunnels)
if (config->cluster_undefined_sessions || config->cluster_undefined_tunnels || config->cluster_undefined_bundles)
return;
config->cluster_iam_uptodate = 1;
@ -520,7 +522,7 @@ void cluster_check_slaves(void)
//
void cluster_check_master(void)
{
int i, count, tcount, high_unique_id = 0;
int i, count, tcount, bcount, high_unique_id = 0;
int last_free = 0;
clockt t = TIME;
static int probed = 0;
@ -614,6 +616,19 @@ void cluster_check_master(void)
config->cluster_highest_tunnelid = i;
}
//
// Go through and mark all the bundles as defined.
// Count the highest used bundle number as well.
//
config->cluster_highest_bundleid = 0;
for (i = 0, bcount = 0; i < MAXBUNDLE; ++i) {
if (bundle[i].state == BUNDLEUNDEF)
bundle[i].state = BUNDLEFREE;
if (bundle[i].state != BUNDLEFREE && i > config->cluster_highest_bundleid)
config->cluster_highest_bundleid = i;
}
//
// Go through and mark all the sessions as being defined.
// reset the idle timeouts.
@ -675,10 +690,11 @@ void cluster_check_master(void)
rebuild_address_pool();
// If we're not the very first master, this is a big issue!
if(count>0)
if (count > 0)
LOG(0, 0, 0, "Warning: Fixed %d uninitialized sessions in becoming master!\n", count);
config->cluster_undefined_sessions = 0;
config->cluster_undefined_bundles = 0;
config->cluster_undefined_tunnels = 0;
config->cluster_iam_uptodate = 1; // assume all peers are up-to-date
@ -696,7 +712,7 @@ void cluster_check_master(void)
// we fix it up here, and we ensure that the 'first free session'
// pointer is valid.
//
static void cluster_check_sessions(int highsession, int freesession_ptr, int hightunnel)
static void cluster_check_sessions(int highsession, int freesession_ptr, int highbundle, int hightunnel)
{
int i;
@ -705,7 +721,7 @@ static void cluster_check_sessions(int highsession, int freesession_ptr, int hig
if (config->cluster_iam_uptodate)
return;
if (highsession > config->cluster_undefined_sessions && hightunnel > config->cluster_undefined_tunnels)
if (highsession > config->cluster_undefined_sessions && highbundle > config->cluster_undefined_bundles && hightunnel > config->cluster_undefined_tunnels)
return;
// Clear out defined sessions, counting the number of
@ -721,6 +737,19 @@ static void cluster_check_sessions(int highsession, int freesession_ptr, int hig
++config->cluster_undefined_sessions;
}
// Clear out defined bundles, counting the number of
// undefs remaining.
config->cluster_undefined_bundles = 0;
for (i = 1 ; i < MAXBUNDLE; ++i) {
if (i > highbundle) {
if (bundle[i].state == BUNDLEUNDEF) bundle[i].state = BUNDLEFREE; // Defined.
continue;
}
if (bundle[i].state == BUNDLEUNDEF)
++config->cluster_undefined_bundles;
}
// Clear out defined tunnels, counting the number of
// undefs remaining.
config->cluster_undefined_tunnels = 0;
@ -735,9 +764,9 @@ static void cluster_check_sessions(int highsession, int freesession_ptr, int hig
}
if (config->cluster_undefined_sessions || config->cluster_undefined_tunnels) {
LOG(2, 0, 0, "Cleared undefined sessions/tunnels. %d sess (high %d), %d tunn (high %d)\n",
config->cluster_undefined_sessions, highsession, config->cluster_undefined_tunnels, hightunnel);
if (config->cluster_undefined_sessions || config->cluster_undefined_tunnels || config->cluster_undefined_bundles) {
LOG(2, 0, 0, "Cleared undefined sessions/bundles/tunnels. %d sess (high %d), %d bund (high %d), %d tunn (high %d)\n",
config->cluster_undefined_sessions, highsession, config->cluster_undefined_bundles, highbundle, config->cluster_undefined_tunnels, hightunnel);
return;
}
@ -770,6 +799,27 @@ static int hb_add_type(uint8_t **p, int type, int id)
add_type(p, C_SESSION, id, (uint8_t *) &session[id], sizeof(sessiont));
break;
case C_CBUNDLE: { // Compressed C_BUNDLE
uint8_t c[sizeof(bundlet) * 2]; // Bigger than worst case.
uint8_t *d = (uint8_t *) &bundle[id];
uint8_t *orig = d;
int size;
size = rle_compress( &d, sizeof(bundlet), c, sizeof(c) );
// Did we compress the full structure, and is the size actually
// reduced??
if ( (d - orig) == sizeof(bundlet) && size < sizeof(bundlet) ) {
add_type(p, C_CBUNDLE, id, c, size);
break;
}
// Failed to compress : Fall through.
}
case C_BUNDLE:
add_type(p, C_BUNDLE, id, (uint8_t *) &bundle[id], sizeof(bundlet));
break;
case C_CTUNNEL: { // Compressed C_TUNNEL
uint8_t c[sizeof(tunnelt) * 2]; // Bigger than worst case.
uint8_t *d = (uint8_t *) &tunnel[id];
@ -802,7 +852,7 @@ static int hb_add_type(uint8_t **p, int type, int id)
//
void cluster_heartbeat()
{
int i, count = 0, tcount = 0;
int i, count = 0, tcount = 0, bcount = 0;
uint8_t buff[MAX_HEART_SIZE + sizeof(heartt) + sizeof(int) ];
heartt h;
uint8_t *p = buff;
@ -823,7 +873,9 @@ void cluster_heartbeat()
h.highsession = config->cluster_highest_sessionid;
h.freesession = sessionfree;
h.hightunnel = config->cluster_highest_tunnelid;
h.highbundle = config->cluster_highest_bundleid;
h.size_sess = sizeof(sessiont); // Just in case.
h.size_bund = sizeof(bundlet);
h.size_tunn = sizeof(tunnelt);
h.interval = config->cluster_hb_interval;
h.timeout = config->cluster_hb_timeout;
@ -878,6 +930,22 @@ void cluster_heartbeat()
++tcount;
}
//
// Fill out the packet with bundles from the bundle table...
while ( (p + sizeof(uint32_t) * 2 + sizeof(bundlet) ) < (buff + MAX_HEART_SIZE) ) {
if (!walk_bundle_number) // bundle #0 isn't valid.
++walk_bundle_number;
if (bcount >= config->cluster_highest_bundleid)
break;
hb_add_type(&p, C_CTUNNEL, walk_bundle_number);
walk_tunnel_number = (1+walk_bundle_number)%(config->cluster_highest_bundleid+1); // +1 avoids divide by zero.
++bcount;
}
//
// Did we do something wrong?
if (p > (buff + sizeof(buff))) { // Did we somehow manage to overun the buffer?
@ -887,9 +955,9 @@ void cluster_heartbeat()
}
LOG(3, 0, 0, "Sending v%d heartbeat #%d, change #%" PRIu64 " with %d changes "
"(%d x-sess, %d x-tunnels, %d highsess, %d hightun, size %d)\n",
"(%d x-sess, %d x-bundles, %d x-tunnels, %d highsess, %d highbund, %d hightun, size %d)\n",
HB_VERSION, h.seq, h.table_version, config->cluster_num_changes,
count, tcount, config->cluster_highest_sessionid,
count, bcount, tcount, config->cluster_highest_sessionid, config->cluster_highest_bundleid,
config->cluster_highest_tunnelid, (int) (p - buff));
config->cluster_num_changes = 0;
@ -938,6 +1006,17 @@ int cluster_send_session(int sid)
return type_changed(C_CSESSION, sid);
}
// A particular bundle has been changed!
int cluster_send_bundle(int bid)
{
if (!config->cluster_iam_master) {
LOG(0, 0, bid, "I'm not a master, but I just tried to change a bundle!\n");
return -1;
}
return type_changed(C_CBUNDLE, bid);
}
// A particular tunnel has been changed!
int cluster_send_tunnel(int tid)
{
@ -1175,6 +1254,31 @@ static int cluster_recv_session(int more, uint8_t *p)
return 0;
}
static int cluster_recv_bundle(int more, uint8_t *p)
{
if (more >= MAXBUNDLE) {
LOG(0, 0, 0, "DANGER: Received a bundle id > MAXBUNDLE!\n");
return -1;
}
if (bundle[more].state == BUNDLEUNDEF) {
if (config->cluster_iam_uptodate) { // Sanity.
LOG(0, 0, 0, "I thought I was uptodate but I just found an undefined bundle!\n");
} else {
--config->cluster_undefined_bundles;
}
}
memcpy(&bundle[more], p, sizeof(bundle[more]) );
LOG(5, 0, more, "Received bundle update\n");
if (!config->cluster_iam_uptodate)
cluster_uptodate(); // Check to see if we're up to date.
return 0;
}
static int cluster_recv_tunnel(int more, uint8_t *p)
{
if (more >= MAXTUNNEL) {
@ -1461,7 +1565,7 @@ static int cluster_process_heartbeat(uint8_t *data, int size, int more, uint8_t
// Check that we don't have too many undefined sessions, and
// that the free session pointer is correct.
cluster_check_sessions(h->highsession, h->freesession, h->hightunnel);
cluster_check_sessions(h->highsession, h->freesession, h->highbundle, h->hightunnel);
if (h->interval != config->cluster_hb_interval)
{
@ -1569,6 +1673,34 @@ static int cluster_process_heartbeat(uint8_t *data, int size, int more, uint8_t
p += sizeof(tunnel[more]);
s -= sizeof(tunnel[more]);
break;
case C_CBUNDLE: { // Compressed bundle structure.
uint8_t c[ sizeof(bundlet) + 2];
int size;
uint8_t *orig_p = p;
size = rle_decompress((uint8_t **) &p, s, c, sizeof(c));
s -= (p - orig_p);
if (size != sizeof(bundlet) ) { // Ouch! Very very bad!
LOG(0, 0, 0, "DANGER: Received a CBUNDLE that didn't decompress correctly!\n");
// Now what? Should exit! No-longer up to date!
break;
}
cluster_recv_bundle(more, c);
break;
}
case C_BUNDLE:
if ( s < sizeof(bundle[more]))
goto shortpacket;
cluster_recv_bundle(more, p);
p += sizeof(bundle[more]);
s -= sizeof(bundle[more]);
break;
default:
LOG(0, 0, 0, "DANGER: I received a heartbeat element where I didn't understand the type! (%d)\n", type);
return -1; // can't process any more of the packet!!
@ -1754,11 +1886,13 @@ int cmd_show_cluster(struct cli_def *cli, char *command, char **argv, int argc)
cli_print(cli, "Table version # : %" PRIu64, config->cluster_table_version);
cli_print(cli, "Next sequence number expected: %d", config->cluster_seq_number);
cli_print(cli, "%d sessions undefined of %d", config->cluster_undefined_sessions, config->cluster_highest_sessionid);
cli_print(cli, "%d bundles undefined of %d", config->cluster_undefined_bundles, config->cluster_highest_bundleid);
cli_print(cli, "%d tunnels undefined of %d", config->cluster_undefined_tunnels, config->cluster_highest_tunnelid);
} else {
cli_print(cli, "Table version # : %" PRIu64, config->cluster_table_version);
cli_print(cli, "Next heartbeat # : %d", config->cluster_seq_number);
cli_print(cli, "Highest session : %d", config->cluster_highest_sessionid);
cli_print(cli, "Highest bundle : %d", config->cluster_highest_bundleid);
cli_print(cli, "Highest tunnel : %d", config->cluster_highest_tunnelid);
cli_print(cli, "%d changes queued for sending", config->cluster_num_changes);
}

View file

@ -1,5 +1,5 @@
// L2TPNS Clustering Stuff
// $Id: cluster.h,v 1.14 2005/07/31 10:04:10 bodea Exp $
// $Id: cluster.h,v 1.15 2006/04/27 09:53:49 bodea Exp $
#ifndef __CLUSTER_H__
#define __CLUSTER_H__
@ -21,6 +21,8 @@
#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 C_BUNDLE 17 // Bundle structure.
#define C_CBUNDLE 18 // Compressed bundle structure.
#define HB_VERSION 5 // Protocol version number..
#define HB_MAX_SEQ (1<<30) // Maximum sequence number. (MUST BE A POWER OF 2!)
@ -43,9 +45,11 @@ typedef struct {
uint32_t highsession; // Id of the highest in-use session.
uint32_t freesession; // Id of the first free session.
uint32_t highbundle; // Id of the highest used bundle.
uint32_t hightunnel; // Id of the highest used tunnel.
uint32_t size_sess; // Size of the session structure.
uint32_t size_bund; // size of the bundle structure.
uint32_t size_tunn; // size of the tunnel structure.
uint32_t interval; // ping/heartbeat interval
uint32_t timeout; // heartbeat timeout
@ -74,6 +78,7 @@ typedef struct {
int cluster_init(void);
int processcluster(uint8_t *buf, int size, in_addr_t addr);
int cluster_send_session(int sid);
int cluster_send_bundle(int bid);
int cluster_send_tunnel(int tid);
int master_forward_packet(uint8_t *data, int size, in_addr_t addr, int port);
int master_forward_dae_packet(uint8_t *data, int size, in_addr_t addr, int port);

View file

@ -41,6 +41,9 @@ set radius_secret "secret"
# Allow multiple logins for the same username
#set allow_duplicate_users no
# Allow multiple logins for specific username
#set guest_account ""
# Write usage accounting files into specified directory
set accounting_dir "/var/run/l2tpns/acct"
@ -96,6 +99,9 @@ set accounting_dir "/var/run/l2tpns/acct"
# Minimum number of slaves before master withdraws routes
#set cluster_master_min_adv 1
# IPv6 address prefix
#set ipv6_prefix ::
# Drop/kill sessions
#load plugin "sessionctl"

4
icmp.c
View file

@ -1,6 +1,6 @@
// L2TPNS: icmp
char const *cvs_id_icmp = "$Id: icmp.c,v 1.10 2005/08/10 11:25:56 bodea Exp $";
char const *cvs_id_icmp = "$Id: icmp.c,v 1.11 2006/04/27 09:53:49 bodea Exp $";
#include <arpa/inet.h>
#include <netdb.h>
@ -114,7 +114,7 @@ void send_ipv6_ra(sessionidt s, tunnelidt t, struct in6_addr *ip)
LOG(3, s, t, "Sending IPv6 RA\n");
memset(b, 0, sizeof(b));
o = makeppp(b, sizeof(b), 0, 0, s, t, PPPIPV6);
o = makeppp(b, sizeof(b), 0, 0, s, t, PPPIPV6, 0, 0, 0);
if (!o)
{

244
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.162 2006/04/23 23:18:31 bodea Exp $";
char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.163 2006/04/27 09:53:49 bodea Exp $";
#include <arpa/inet.h>
#include <assert.h>
@ -127,6 +127,7 @@ config_descriptt config_values[] = {
CONFIG("radius_authtypes", radius_authtypes_s, STRING),
CONFIG("radius_dae_port", radius_dae_port, SHORT),
CONFIG("allow_duplicate_users", allow_duplicate_users, BOOL),
CONFIG("guest_account", guest_user, STRING),
CONFIG("bind_address", bind_address, IPv4),
CONFIG("peer_address", peer_address, IPv4),
CONFIG("send_garp", send_garp, BOOL),
@ -174,6 +175,8 @@ static sessiont shut_acct[8192];
static sessionidt shut_acct_n = 0;
tunnelt *tunnel = NULL; // Array of tunnel structures.
bundlet *bundle = NULL; // Array of bundle structures.
fragmentationt *frag = NULL; // Array of fragmentation structures.
sessiont *session = NULL; // Array of session structures.
sessionlocalt *sess_local = NULL; // Array of local per-session counters.
radiust *radius = NULL; // Array of radius structures.
@ -203,6 +206,7 @@ static void plugins_done(void);
static void processcontrol(uint8_t *buf, int len, struct sockaddr_in *addr, int alen, struct in_addr *local);
static tunnelidt new_tunnel(void);
static void unhide_value(uint8_t *value, size_t len, uint16_t type, uint8_t *vector, size_t vec_len);
static void bundleclear(bundleidt b);
// on slaves, alow BGP to withdraw cleanly before exiting
#define QUIT_DELAY 5
@ -1051,6 +1055,54 @@ void adjust_tcp_mss(sessionidt s, tunnelidt t, uint8_t *buf, int len, uint8_t *t
*(uint16_t *) (tcp + 16) = htons(sum + (sum >> 16));
}
void processmpframe(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l, uint8_t extra)
{
uint16_t proto;
if (extra) {
// Skip the four extra bytes
p += 4;
l -= 4;
}
// Process this frame
if (*p & 1)
{
proto = *p++;
l--;
}
else
{
proto = ntohs(*(uint16_t *) p);
p += 2;
l -= 2;
}
if (proto == PPPIP)
{
if (session[s].die)
{
LOG(4, s, t, "MPPP: Session %d is closing. Don't process PPP packets\n", s);
return; // closing session, PPP not processed
}
session[s].last_packet = time_now;
processipin(s, t, p, l);
}
else if (proto == PPPIPV6 && config->ipv6_prefix.s6_addr[0])
{
if (session[s].die)
{
LOG(4, s, t, "MPPP: Session %d is closing. Don't process PPP packets\n", s);
return; // closing session, PPP not processed
}
session[s].last_packet = time_now;
processipv6in(s, t, p, l);
}
else
{
LOG(2, s, t, "MPPP: Unsupported MP protocol 0x%04X received\n",proto);
}
}
// process outgoing (to tunnel) IP
//
static void processipout(uint8_t *buf, int len)
@ -1063,7 +1115,8 @@ static void processipout(uint8_t *buf, int len)
uint8_t *data = buf; // Keep a copy of the originals.
int size = len;
uint8_t b[MAXETHER + 20];
uint8_t b1[MAXETHER + 20];
uint8_t b2[MAXETHER + 20];
CSTAT(processipout);
@ -1183,13 +1236,43 @@ static void processipout(uint8_t *buf, int len)
return;
}
LOG(5, s, t, "Ethernet -> Tunnel (%d bytes)\n", len);
// Add on L2TP header
{
uint8_t *p = makeppp(b, sizeof(b), buf, len, s, t, PPPIP);
bundleidt bid = 0;
if (session[s].bundle && bundle[session[s].bundle].num_of_links > 1)
{
bid = session[s].bundle;
s = bundle[bid].members[bundle[bid].current_ses = ++bundle[bid].current_ses % bundle[bid].num_of_links];
LOG(4, s, t, "MPPP: (1)Session number becomes: %d\n", s);
if (len > 256)
{
// Partition the packet to 2 fragments
uint32_t frag1len = len / 2;
uint32_t frag2len = len - frag1len;
uint8_t *p = makeppp(b1, sizeof(b1), buf, frag1len, s, t, PPPIP, 0, bid, MP_BEGIN);
uint8_t *q;
if (!p) return;
tunnelsend(b, len + (p-b), t); // send it...
tunnelsend(b1, frag1len + (p-b1), t); // send it...
s = bundle[bid].members[bundle[bid].current_ses = ++bundle[bid].current_ses % bundle[bid].num_of_links];
LOG(4, s, t, "MPPP: (2)Session number becomes: %d\n", s);
q = makeppp(b2, sizeof(b2), buf+frag1len, frag2len, s, t, PPPIP, 0, bid, MP_END);
if (!q) return;
tunnelsend(b2, frag2len + (q-b2), t); // send it...
}
else {
// Send it as one frame
uint8_t *p = makeppp(b1, sizeof(b1), buf, len, s, t, PPPIP, 0, bid, MP_BOTH_BITS);
if (!p) return;
tunnelsend(b1, len + (p-b1), t); // send it...
}
}
else
{
uint8_t *p = makeppp(b1, sizeof(b1), buf, len, s, t, PPPIP, 0, 0, 0);
if (!p) return;
tunnelsend(b1, len + (p-b1), t); // send it...
}
}
// Snooping this session, send it to intercept box
@ -1273,6 +1356,12 @@ static void processipv6out(uint8_t * buf, int len)
}
return;
}
if (session[s].bundle && bundle[session[s].bundle].num_of_links > 1)
{
bundleidt bid = session[s].bundle;
s = bundle[bid].members[bundle[bid].current_ses = ++bundle[bid].current_ses % bundle[bid].num_of_links];
LOG(3, s, session[s].tunnel, "MPPP: Session number becomes: %d\n", s);
}
t = session[s].tunnel;
sp = &session[s];
@ -1298,7 +1387,7 @@ static void processipv6out(uint8_t * buf, int len)
// Add on L2TP header
{
uint8_t *p = makeppp(b, sizeof(b), buf, len, s, t, PPPIPV6);
uint8_t *p = makeppp(b, sizeof(b), buf, len, s, t, PPPIPV6, 0, 0, 0);
if (!p) return;
tunnelsend(b, len + (p-b), t); // send it...
}
@ -1350,7 +1439,7 @@ static void send_ipout(sessionidt s, uint8_t *buf, int len)
// Add on L2TP header
{
uint8_t *p = makeppp(b, sizeof(b), buf, len, s, t, PPPIP);
uint8_t *p = makeppp(b, sizeof(b), buf, len, s, t, PPPIP, 0, 0, 0);
if (!p) return;
tunnelsend(b, len + (p-b), t); // send it...
}
@ -1679,7 +1768,7 @@ void sendipcp(sessionidt s, tunnelidt t)
session[s].unique_id = last_id;
}
q = makeppp(buf, sizeof(buf), 0, 0, s, t, PPPIPCP);
q = makeppp(buf, sizeof(buf), 0, 0, s, t, PPPIPCP, 0, 0, 0);
if (!q) return;
*q = ConfigReq;
@ -1703,7 +1792,7 @@ void sendipv6cp(sessionidt s, tunnelidt t)
CSTAT(sendipv6cp);
LOG(3, s, t, "IPV6CP: send ConfigReq\n");
q = makeppp(buf, sizeof(buf), 0, 0, s, t, PPPIPV6CP);
q = makeppp(buf, sizeof(buf), 0, 0, s, t, PPPIPV6CP, 0, 0, 0);
if (!q) return;
*q = ConfigReq;
@ -1735,6 +1824,7 @@ static void sessionclear(sessionidt s)
// kill a session now
void sessionkill(sessionidt s, char *reason)
{
bundleidt b;
CSTAT(sessionkill);
@ -1753,6 +1843,38 @@ void sessionkill(sessionidt s, char *reason)
radiusclear(sess_local[s].radius, s); // cant send clean accounting data, session is killed
LOG(2, s, session[s].tunnel, "Kill session %d (%s): %s\n", s, session[s].user, reason);
if ((b = session[s].bundle))
{
// This session was part of a bundle
bundle[b].num_of_links--;
LOG(3, s, 0, "MPPP: Dropping member link: %d from bundle %d\n",s,b);
if (bundle[b].num_of_links == 0)
{
bundleclear(b);
LOG(3, s, 0, "MPPP: Kill bundle: %d (No remaing member links)\n",b);
}
else
{
// Adjust the members array to accomodate the new change
uint8_t mem_num = 0;
// It should be here num_of_links instead of num_of_links-1 (previous instruction "num_of_links--")
if (bundle[b].members[bundle[b].num_of_links] != s)
{
uint8_t ml;
for (ml = 0; ml<bundle[b].num_of_links; ml++)
{
if (bundle[b].members[ml] == s)
{
mem_num = ml;
break;
}
}
bundle[b].members[mem_num] = bundle[b].members[bundle[b].num_of_links];
LOG(3, s, 0, "MPPP: Adjusted member links array\n");
}
}
cluster_send_bundle(b);
}
sessionclear(s);
cluster_send_session(s);
}
@ -1764,6 +1886,13 @@ static void tunnelclear(tunnelidt t)
tunnel[t].state = TUNNELFREE;
}
static void bundleclear(bundleidt b)
{
if (!b) return;
memset(&bundle[b], 0, sizeof(bundle[b]));
bundle[b].state = BUNDLEFREE;
}
// kill a tunnel now
static void tunnelkill(tunnelidt t, char *reason)
{
@ -2556,6 +2685,11 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr)
// start LCP
sess_local[s].lcp_authtype = config->radius_authprefer;
sess_local[s].ppp_mru = MRU;
// Set multilink options before sending initial LCP packet
sess_local[s].mp_mrru = 1614;
sess_local[s].mp_epdis = config->bind_address ? config->bind_address : my_address;
sendlcp(s, t);
change_state(s, lcp, RequestSent);
break;
@ -2679,6 +2813,23 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr)
processipin(s, t, p, l);
}
else if (proto == PPPMP)
{
if (session[s].die)
{
LOG(4, s, t, "Session %d is closing. Don't process PPP packets\n", s);
return; // closing session, PPP not processed
}
session[s].last_packet = time_now;
if (session[s].walled_garden && !config->cluster_iam_master)
{
master_forward_packet(buf, len, addr->sin_addr.s_addr, addr->sin_port);
return;
}
processmpin(s, t, p, l);
}
else if (proto == PPPIPV6 && config->ipv6_prefix.s6_addr[0])
{
if (session[s].die)
@ -2873,6 +3024,37 @@ static void regular_cleanups(double period)
continue;
}
// check for timed out sessions
if (session[s].timeout)
{
bundleidt bid = session[s].bundle;
if (bid)
{
clockt curr_time = time_now;
if (curr_time - bundle[bid].last_check >= 1)
{
bundle[bid].online_time += (curr_time-bundle[bid].last_check)*bundle[bid].num_of_links;
bundle[bid].last_check = curr_time;
if (bundle[bid].online_time >= session[s].timeout)
{
int ses;
for (ses = bundle[bid].num_of_links - 1; ses >= 0; ses--)
{
sessionshutdown(bundle[bid].members[ses], "Session timeout", CDN_ADMIN_DISC, TERM_SESSION_TIMEOUT);
s_actions++;
continue;
}
}
}
}
else if (session[s].timeout <= time_now - session[s].opened)
{
sessionshutdown(s, "Session timeout", CDN_ADMIN_DISC, TERM_SESSION_TIMEOUT);
s_actions++;
continue;
}
}
// PPP timeouts
if (sess_local[s].lcp.restart <= time_now)
{
@ -2999,7 +3181,7 @@ static void regular_cleanups(double period)
{
uint8_t b[MAXETHER];
uint8_t *q = makeppp(b, sizeof(b), 0, 0, s, session[s].tunnel, PPPLCP);
uint8_t *q = makeppp(b, sizeof(b), 0, 0, s, session[s].tunnel, PPPLCP, 1, 0, 0);
if (!q) continue;
*q = EchoReq;
@ -3714,6 +3896,16 @@ static void initdata(int optdebug, char *optconfig)
LOG(0, 0, 0, "Error doing malloc for tunnels: %s\n", strerror(errno));
exit(1);
}
if (!(bundle = shared_malloc(sizeof(bundlet) * MAXBUNDLE)))
{
LOG(0, 0, 0, "Error doing malloc for bundles: %s\n", strerror(errno));
exit(1);
}
if (!(frag = shared_malloc(sizeof(fragmentationt) * MAXBUNDLE)))
{
LOG(0, 0, 0, "Error doing malloc for fragmentations: %s\n", strerror(errno));
exit(1);
}
if (!(session = shared_malloc(sizeof(sessiont) * MAXSESSION)))
{
LOG(0, 0, 0, "Error doing malloc for sessions: %s\n", strerror(errno));
@ -3760,6 +3952,7 @@ static void initdata(int optdebug, char *optconfig)
memset(cli_tunnel_actions, 0, sizeof(struct cli_tunnel_actions) * MAXSESSION);
memset(tunnel, 0, sizeof(tunnelt) * MAXTUNNEL);
memset(bundle, 0, sizeof(bundlet) * MAXBUNDLE);
memset(session, 0, sizeof(sessiont) * MAXSESSION);
memset(radius, 0, sizeof(radiust) * MAXRADIUS);
memset(ip_address_pool, 0, sizeof(ippoolt) * MAXIPPOOL);
@ -3777,6 +3970,10 @@ static void initdata(int optdebug, char *optconfig)
for (i = 1; i < MAXTUNNEL; i++)
tunnel[i].state = TUNNELUNDEF; // mark it as not filled in.
for (i = 1; i < MAXBUNDLE; i++) {
bundle[i].state = BUNDLEUNDEF;
}
if (!*hostname)
{
// Grab my hostname unless it's been specified
@ -4601,6 +4798,14 @@ int sessionsetup(sessionidt s, tunnelidt t)
// Make sure this is right
session[s].tunnel = t;
// Join a bundle if the MRRU option is accepted
if (session[s].mrru > 0 && !session[s].bundle)
{
LOG(3, s, t, "This session can be part of multilink bundle\n");
if (join_bundle(s))
cluster_send_bundle(session[s].bundle);
}
// zap old sessions with same IP and/or username
// Don't kill gardened sessions - doing so leads to a DoS
// from someone who doesn't need to know the password
@ -4617,8 +4822,21 @@ int sessionsetup(sessionidt s, tunnelidt t)
continue;
}
if (config->allow_duplicate_users) continue;
if (session[s].walled_garden || session[i].walled_garden) continue;
if (config->allow_duplicate_users)
continue;
if (session[s].walled_garden || session[i].walled_garden)
continue;
// Allow duplicate sessions for guest account.
if (*config->guest_user && !strcasecmp(user, config->guest_user))
continue;
// Allow duplicate sessions for multilink ones of the same bundle.
if (session[s].bundle && session[i].bundle && session[s].bundle == session[i].bundle)
continue;
// Drop the new session in case of duplicate sessionss, not the old one.
if (!strcasecmp(user, session[i].user))
sessionkill(i, "Duplicate session for users");
}

103
l2tpns.h
View file

@ -1,5 +1,5 @@
// L2TPNS Global Stuff
// $Id: l2tpns.h,v 1.114 2006/04/23 23:18:32 bodea Exp $
// $Id: l2tpns.h,v 1.115 2006/04/27 09:53:50 bodea Exp $
#ifndef __L2TPNS_H__
#define __L2TPNS_H__
@ -14,10 +14,15 @@
#include <sys/types.h>
#include <libcli.h>
#define VERSION "2.1.19"
#define VERSION "2.2.0"
// Limits
#define MAXTUNNEL 500 // could be up to 65535
#define MAXBUNDLE 300 // could be up to 65535
#define MAXBUNDLESES 10 // Maximum number of member links in bundle
#define MAXADDRESS 20 // Maximum length for the Endpoint Discrminiator address
#define MAXFRAGNUM 1500 // Maximum number of Multilink fragment in a bundle
#define MAXFRAGLEN 1000 // Maximum length for Multilink fragment
#define MAXSESSION 60000 // could be up to 65535
#define MAXTBFS 6000 // Maximum token bucket filters. Might need up to 2 * session.
@ -43,10 +48,16 @@
#define MAXIPPOOL 131072 // max number of ip addresses in pool
#define RINGBUFFER_SIZE 10000 // Number of ringbuffer entries to allocate
#define MAX_LOG_LENGTH 512 // Maximum size of log message
#define ECHO_TIMEOUT 60 // Time between last packet sent and LCP ECHO generation
#define ECHO_TIMEOUT 10 // Time between last packet sent and LCP ECHO generation
#define IDLE_TIMEOUT 240 // Time between last packet sent and LCP ECHO generation
#define BUSY_WAIT_TIME 3000 // 5 minutes in 1/10th seconds to wait for radius to cleanup on shutdown
#define MP_BEGIN 0x80 // This value is used when (b)egin bit is set in MP header
#define MP_END 0x40 // This value is used when (e)nd bit is set in MP header
#define MP_BOTH_BITS 0xC0 // This value is used when both bits (begin and end) are set in MP header
#define DEFAULT_EPDIS_ADDRESS "L2TPNetServer" // Company name may goes here!
// Constants
#ifndef ETCDIR
#define ETCDIR "/etc/l2tpns"
@ -186,6 +197,7 @@ enum {
// Types
typedef uint16_t sessionidt;
typedef uint16_t bundleidt;
typedef uint16_t tunnelidt;
typedef uint32_t clockt;
typedef uint8_t hasht[16];
@ -231,6 +243,17 @@ typedef struct controls // control message
}
controlt;
typedef struct {
uint8_t length; // Endpoint Discriminator length
uint8_t addr_class; // Endpoint Discriminator class
uint8_t address[MAXADDRESS]; // Endpoint Discriminator address
} epdist;
typedef struct {
uint16_t length; // Fragment length
uint8_t data[MAXFRAGLEN]; // Fragment data
} fragmentt;
typedef struct
{
sessionidt next; // next session in linked list
@ -273,6 +296,11 @@ typedef struct
char calling[MAXTEL]; // calling number
uint32_t tx_connect_speed;
uint32_t rx_connect_speed;
clockt timeout; // Session timeout
uint32_t mrru; // Multilink Max-Receive-Reconstructed-Unit
uint8_t mssf; // Multilink Short Sequence Number Header Format
epdist epdis; // Multilink Endpoint Discriminator
bundleidt bundle; // Multilink Bundle Identifier
in_addr_t snoop_ip; // Interception destination IP
uint16_t snoop_port; // Interception destination port
uint8_t walled_garden; // is this session gardened?
@ -282,6 +310,33 @@ typedef struct
}
sessiont;
typedef struct
{
int state; // current state (bundlestate enum)
uint32_t seq_num_t; // Sequence Number (transmission)
uint32_t seq_num_m; // Last received frame sequence number (bearing B bit)
uint32_t offset; // Offset between sequence number and array index
uint8_t pending_frag; // Indicate that there is pending fragments to reassemble
uint8_t num_of_links; // Number of links joint to this bundle
uint32_t online_time; // The time this bundle is online
clockt last_check; // Last time the timeout is checked
uint32_t mrru; // Multilink Max-Receive-Reconstructed-Unit
uint8_t mssf; // Multilink Short Sequence Number Header Format
epdist epdis; // Multilink Endpoint Discriminator
char user[MAXUSER]; // Needed for matching member links
sessionidt current_ses; // Current session to use for sending (used in RR load-balancing)
sessionidt members[MAXBUNDLESES]; // Array for member links sessions
}
bundlet;
typedef struct
{
fragmentt fragment[MAXFRAGNUM];
uint8_t reassembled_frame[MAXETHER]; // The reassembled frame
uint16_t re_frame_len; // The reassembled frame length
}
fragmentationt;
#define AUTHPAP 1 // allow PAP
#define AUTHCHAP 2 // allow CHAP
@ -311,6 +366,15 @@ typedef struct
// our MRU
uint16_t ppp_mru;
// our MRRU
uint16_t mp_mrru;
// our mssf
uint16_t mp_mssf;
// our Endpoint Discriminator
in_addr_t mp_epdis;
// DoS prevention
clockt last_packet_out;
uint32_t packets_out;
@ -407,6 +471,23 @@ enum
TUNNELUNDEF, // Undefined
};
enum
{
BUNDLEFREE, // Not in use
BUNDLEOPEN, // Active bundle
BUNDLEUNDEF, // Undefined
};
enum
{
NULLCLASS = 0, //End Point Discriminator classes
LOCALADDR,
IPADDR,
IEEEMACADDR,
PPPMAGIC,
PSNDN,
};
enum
{
RADIUSNULL, // Not in use
@ -561,6 +642,7 @@ typedef struct
int radius_authprefer;
int allow_duplicate_users; // allow multiple logins with the same username
char guest_user[MAXUSER]; // allow multiple logins to guest account username
in_addr_t default_dns1, default_dns2;
@ -583,6 +665,7 @@ typedef struct
int lock_pages; // Lock pages into memory.
int icmp_rate; // Max number of ICMP unreachable per second to send
int max_packets; // DoS prevention: per session limit of packets/0.1s
char epdis_addr[20]; // MP Endpoint Discriminator address
in_addr_t cluster_address; // Multicast address of cluster.
// Send to this address to have everyone hear.
@ -594,8 +677,10 @@ typedef struct
int cluster_seq_number; // Sequence number of the next heartbeat we'll send out
// (or the seq number we're next expecting if we're a slave).
int cluster_undefined_sessions; // How many sessions we're yet to receive from the master.
int cluster_undefined_bundles; // How many bundles we're yet to receive from the master.
int cluster_undefined_tunnels; // How many tunnels we're yet to receive from the master.
int cluster_highest_sessionid;
int cluster_highest_bundleid;
int cluster_highest_tunnelid;
clockt cluster_last_hb; // Last time we saw a heartbeat from the master.
int cluster_last_hb_ver; // Heartbeat version last seen from master
@ -605,13 +690,11 @@ typedef struct
int cluster_hb_interval; // How often to send a heartbeat.
int cluster_hb_timeout; // How many missed heartbeats trigger an election.
uint64_t cluster_table_version; // # state changes processed by cluster
struct in6_addr ipv6_prefix; // Our IPv6 network pool.
int cluster_master_min_adv; // Master advertises routes while the number of up to date
// slaves is less than this value.
struct in6_addr ipv6_prefix; // Our IPv6 network pool.
#ifdef BGP
#define BGP_NUM_PEERS 2
uint16_t as_number;
@ -727,14 +810,17 @@ void processlcp(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l);
void processipcp(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l);
void processipv6cp(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l);
void processipin(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l);
void processmpin(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l);
void processmpframe(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l, uint8_t extra);
void processipv6in(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l);
void processccp(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l);
void sendchap(sessionidt s, tunnelidt t);
uint8_t *makeppp(uint8_t *b, int size, uint8_t *p, int l, sessionidt s, tunnelidt t, uint16_t mtype);
uint8_t *makeppp(uint8_t *b, int size, uint8_t *p, int l, sessionidt s, tunnelidt t, uint16_t mtype, uint8_t prio, bundleidt bid, uint8_t mp_bits);
void sendlcp(sessionidt s, tunnelidt t);
void send_ipin(sessionidt s, uint8_t *buf, int len);
void sendccp(sessionidt s, tunnelidt t);
void protoreject(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l, uint16_t proto);
int join_bundle(sessionidt s);
// radius.c
@ -802,6 +888,7 @@ void host_unreachable(in_addr_t destination, uint16_t id, in_addr_t source, uint
extern tunnelt *tunnel;
extern bundlet *bundle;
extern sessiont *session;
extern sessionlocalt *sess_local;
extern ippoolt *ip_address_pool;

View file

@ -1,6 +1,6 @@
Summary: A high-speed clustered L2TP LNS
Name: l2tpns
Version: 2.1.19
Version: 2.2.0
Release: 1
License: GPL
Group: System Environment/Daemons
@ -43,5 +43,5 @@ rm -rf %{buildroot}
%attr(644,root,root) /usr/share/man/man[58]/*
%changelog
* Tue Apr 25 2006 Brendan O'Dea <bod@optus.net> 2.1.19-1
- 2.1.19 release, see /usr/share/doc/l2tpns-2.1.19/Changes
* Thu Apr 27 2006 Brendan O'Dea <bod@optus.net> 2.2.0-1
- 2.2.0 release, see /usr/share/doc/l2tpns-2.2.0/Changes

513
ppp.c
View file

@ -1,6 +1,6 @@
// L2TPNS PPP Stuff
char const *cvs_id_ppp = "$Id: ppp.c,v 1.99 2006/04/18 06:00:08 bodea Exp $";
char const *cvs_id_ppp = "$Id: ppp.c,v 1.100 2006/04/27 09:53:50 bodea Exp $";
#include <stdio.h>
#include <string.h>
@ -15,6 +15,8 @@ char const *cvs_id_ppp = "$Id: ppp.c,v 1.99 2006/04/18 06:00:08 bodea Exp $";
#include "cluster.h"
extern tunnelt *tunnel;
extern bundlet *bundle;
extern fragmentationt *frag;
extern sessiont *session;
extern radiust *radius;
extern int tunfd;
@ -24,6 +26,10 @@ extern time_t time_now;
extern configt *config;
static int add_lcp_auth(uint8_t *b, int size, int authtype);
static bundleidt new_bundle(void);
static int epdiscmp(epdist, epdist);
static void setepdis(epdist *, epdist);
static void ipcp_open(sessionidt s, tunnelidt t);
// Process PAP messages
void processpap(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l)
@ -90,7 +96,7 @@ void processpap(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l)
// respond now, either no RADIUS available or already authenticated
uint8_t b[MAXETHER];
uint8_t id = p[1];
uint8_t *p = makeppp(b, sizeof(b), 0, 0, s, t, PPPPAP);
uint8_t *p = makeppp(b, sizeof(b), 0, 0, s, t, PPPPAP, 0, 0, 0);
if (!p) return;
if (session[s].ip)
@ -351,6 +357,18 @@ void lcp_open(sessionidt s, tunnelidt t)
sendchap(s, t);
}
else
{
if (session[s].bundle && bundle[session[s].bundle].num_of_links > 1)
{
sessionidt first_ses = bundle[session[s].bundle].members[0];
LOG(3, s, t, "MPPP: Skipping IPCP negotiation for session:%d, first session of bundle is:%d\n", s, first_ses);
session[s].ip = session[first_ses].ip;
session[s].dns1 = session[first_ses].dns1;
session[s].dns2 = session[first_ses].dns2;
session[s].timeout = session[first_ses].timeout;
ipcp_open(s, t);
}
else
{
// This-Layer-Up
sendipcp(s, t);
@ -363,6 +381,7 @@ void lcp_open(sessionidt s, tunnelidt t)
change_state(s, ccp, Stopped);
}
}
}
static void lcp_restart(sessionidt s)
@ -379,7 +398,7 @@ static uint8_t *ppp_conf_rej(sessionidt s, uint8_t *buf, size_t blen, uint16_t m
{
if (!*response || **response != ConfigRej)
{
queued = *response = makeppp(buf, blen, packet, 2, s, session[s].tunnel, mtype);
queued = *response = makeppp(buf, blen, packet, 2, s, session[s].tunnel, mtype, 0, 0, 0);
if (!queued)
return 0;
@ -423,7 +442,7 @@ static uint8_t *ppp_conf_nak(sessionidt s, uint8_t *buf, size_t blen, uint16_t m
if (*nak_sent >= config->ppp_max_failure)
return ppp_conf_rej(s, buf, blen, mtype, response, 0, packet, option);
queued = *response = makeppp(buf, blen, packet, 2, s, session[s].tunnel, mtype);
queued = *response = makeppp(buf, blen, packet, 2, s, session[s].tunnel, mtype, 0, 0, 0);
if (!queued)
return 0;
@ -455,7 +474,7 @@ static void ppp_code_rej(sessionidt s, tunnelidt t, uint16_t proto,
l += 4;
if (l > mru) l = mru;
q = makeppp(buf, size, 0, 0, s, t, proto);
q = makeppp(buf, size, 0, 0, s, t, proto, 0, 0, 0);
if (!q) return;
*q = CodeRej;
@ -567,6 +586,7 @@ void processlcp(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l)
uint8_t *response = 0;
static uint8_t asyncmap[4] = { 0, 0, 0, 0 }; // all zero
static uint8_t authproto[5];
int changed = 0;
while (x > 2)
{
@ -582,7 +602,7 @@ void processlcp(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l)
if (mru >= MINMTU)
{
session[s].mru = mru;
cluster_send_session(s);
changed++;
break;
}
@ -655,6 +675,64 @@ void processlcp(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l)
case 8: // Address-And-Control-Field-Compression
break;
case 17: // Multilink Max-Receive-Reconstructed-Unit
{
uint16_t mrru = ntohs(*(uint16_t *)(o + 2));
session[s].mrru = mrru;
changed++;
LOG(3, s, t, " Received PPP LCP option MRRU: %d\n",mrru);
}
break;
case 18: // Multilink Short Sequence Number Header Format
{
session[s].mssf = 1;
changed++;
LOG(3, s, t, " Received PPP LCP option MSSN format\n");
}
break;
case 19: // Multilink Endpoint Discriminator
{
uint8_t epdis_class = o[2];
int addr;
session[s].epdis.addr_class = epdis_class;
session[s].epdis.length = length - 3;
if (session[s].epdis.length > 20)
{
LOG(1, s, t, "Error: received EndDis Address Length more than 20: %d\n", session[s].epdis.length);
session[s].epdis.length = 20;
}
for (addr = 0; addr < session[s].epdis.length; addr++)
session[s].epdis.address[addr] = o[3+addr];
changed++;
switch (epdis_class)
{
case LOCALADDR:
LOG(3, s, t, " Received PPP LCP option Multilink EndDis Local Address Class: %d\n",epdis_class);
break;
case IPADDR:
LOG(3, s, t, " Received PPP LCP option Multilink EndDis IP Address Class: %d\n",epdis_class);
break;
case IEEEMACADDR:
LOG(3, s, t, " Received PPP LCP option Multilink EndDis IEEE MAC Address Class: %d\n",epdis_class);
break;
case PPPMAGIC:
LOG(3, s, t, " Received PPP LCP option Multilink EndDis PPP Magic No Class: %d\n",epdis_class);
break;
case PSNDN:
LOG(3, s, t, " Received PPP LCP option Multilink EndDis PSND No Class: %d\n",epdis_class);
break;
default:
LOG(3, s, t, " Received PPP LCP option Multilink EndDis NULL Class %d\n",epdis_class);
}
}
break;
default: // Reject any unknown options
LOG(3, s, t, " Rejecting unknown PPP LCP option %d\n", type);
q = ppp_conf_rej(s, b, sizeof(b), PPPLCP, &response, q, p, o);
@ -663,6 +741,9 @@ void processlcp(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l)
o += length;
}
if (changed)
cluster_send_session(s);
if (response)
{
l = q - response; // LCP packet length
@ -671,7 +752,7 @@ void processlcp(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l)
else
{
// Send packet back as ConfigAck
response = makeppp(b, sizeof(b), p, l, s, t, PPPLCP);
response = makeppp(b, sizeof(b), p, l, s, t, PPPLCP, 0, 0, 0);
if (!response) return;
*response = ConfigAck;
}
@ -679,7 +760,7 @@ void processlcp(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l)
switch (session[s].ppp.lcp)
{
case Closed:
response = makeppp(b, sizeof(b), p, 2, s, t, PPPLCP);
response = makeppp(b, sizeof(b), p, 2, s, t, PPPLCP, 0, 0, 0);
if (!response) return;
*response = TerminateAck;
*((uint16_t *) (response + 2)) = htons(l = 4);
@ -812,6 +893,50 @@ void processlcp(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l)
cluster_send_session(s);
break;
case 17: // Multilink Max-Receive-Reconstructed-Unit
{
if (*p == ConfigNak)
{
sess_local[s].mp_mrru = ntohs(*(uint16_t *)(o + 2));
LOG(3, s, t, " Remote requested MRRU of %u\n", sess_local[s].mp_mrru);
}
else
{
sess_local[s].mp_mrru = 0;
LOG(3, s, t, " Remote rejected MRRU negotiation\n");
}
}
break;
case 18: // Multilink Short Sequence Number Header Format
{
if (*p == ConfigNak)
{
sess_local[s].mp_mssf = 0;
LOG(3, s, t, " Remote requested Naked mssf\n");
}
else
{
sess_local[s].mp_mssf = 0;
LOG(3, s, t, " Remote rejected mssf\n");
}
}
break;
case 19: // Multilink Endpoint Discriminator
{
if (*p == ConfigNak)
{
LOG(2, s, t, " Remote should not configNak Endpoint Dis!\n");
}
else
{
sess_local[s].mp_epdis = 0;
LOG(3, s, t, " Remote rejected Endpoint Discriminator\n");
}
}
break;
default:
LOG(2, s, t, "LCP: remote sent %s for type %u?\n", ppp_code(*p), type);
sessionshutdown(s, "Unable to negotiate LCP.", CDN_ADMIN_DISC, TERM_USER_ERROR);
@ -835,7 +960,7 @@ void processlcp(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l)
case Closed:
case Stopped:
{
uint8_t *response = makeppp(b, sizeof(b), p, 2, s, t, PPPLCP);
uint8_t *response = makeppp(b, sizeof(b), p, 2, s, t, PPPLCP, 0, 0, 0);
if (!response) return;
*response = TerminateAck;
*((uint16_t *) (response + 2)) = htons(l = 4);
@ -893,7 +1018,7 @@ void processlcp(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l)
}
*p = TerminateAck; // send ack
q = makeppp(b, sizeof(b), p, l, s, t, PPPLCP);
q = makeppp(b, sizeof(b), p, l, s, t, PPPLCP, 0, 0, 0);
if (!q) return;
LOG(3, s, t, "LCP: send %s\n", ppp_code(*q));
@ -929,7 +1054,7 @@ void processlcp(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l)
{
*p = EchoReply; // reply
*(uint32_t *) (p + 4) = htonl(session[s].magic); // our magic number
q = makeppp(b, sizeof(b), p, l, s, t, PPPLCP);
q = makeppp(b, sizeof(b), p, l, s, t, PPPLCP, 0, 0, 0);
if (!q) return;
LOG(4, s, t, "LCP: send %s\n", ppp_code(*q));
@ -947,6 +1072,93 @@ void processlcp(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l)
}
}
int join_bundle(sessionidt s)
{
// Search for a bundle to join
bundleidt i;
bundleidt b;
for (i = 1; i < MAXBUNDLE; i++)
{
if (bundle[i].state != BUNDLEFREE)
{
if (epdiscmp(session[s].epdis,bundle[i].epdis) && !strcmp(session[s].user, bundle[i].user))
{
session[s].bundle = i;
bundle[i].mrru = session[s].mrru;
bundle[i].mssf = session[s].mssf;
if (session[s].epdis.length > 0)
setepdis(&bundle[i].epdis, session[s].epdis);
strcpy(bundle[i].user, session[s].user);
bundle[i].members[bundle[i].num_of_links] = s;
bundle[i].num_of_links++;
LOG(3, s, session[s].tunnel, "MPPP: Bundling additional line in bundle (%d), lines:%d\n",i,bundle[i].num_of_links);
return i;
}
}
}
// No previously created bundle was found for this session, so create a new one
if (!(b = new_bundle())) return 0;
session[s].bundle = b;
bundle[b].mrru = session[s].mrru;
bundle[b].mssf = session[s].mssf;
if (session[s].epdis.length > 0)
setepdis(&bundle[b].epdis, session[s].epdis);
strcpy(bundle[b].user, session[s].user);
bundle[b].members[0] = s;
LOG(3, s, session[s].tunnel, "MPPP: Created a new bundle (%d)\n", b);
return b;
}
static int epdiscmp(epdist ep1, epdist ep2)
{
int ad;
if (ep1.length != ep2.length)
return 0;
if (ep1.addr_class != ep2.addr_class)
return 0;
for (ad = 0; ad < ep1.length; ad++)
if (ep1.address[ad] != ep2.address[ad])
return 0;
return 1;
}
static void setepdis(epdist *ep1, epdist ep2)
{
int ad;
ep1->length = ep2.length;
ep1->addr_class = ep2.addr_class;
for (ad = 0; ad < ep2.length; ad++)
ep1->address[ad] = ep2.address[ad];
}
static bundleidt new_bundle()
{
bundleidt i;
for (i = 1; i < MAXBUNDLE; i++)
{
if (bundle[i].state == BUNDLEFREE)
{
LOG(4, 0, 0, "MPPP: Assigning bundle ID %d\n", i);
bundle[i].num_of_links = 1;
bundle[i].last_check = time_now; // Initialize last_check value
bundle[i].state = BUNDLEOPEN;
bundle[i].current_ses = -1; // This is to enforce the first session 0 to be used at first
if (i > config->cluster_highest_bundleid)
config->cluster_highest_bundleid = i;
return i;
}
}
LOG(0, 0, 0, "MPPP: Can't find a free bundle! There shouldn't be this many in use!\n");
return 0;
}
static void ipcp_open(sessionidt s, tunnelidt t)
{
LOG(3, s, t, "IPCP: Opened, session is now active\n");
@ -1105,7 +1317,7 @@ void processipcp(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l)
else if (gotip)
{
// Send packet back as ConfigAck
response = makeppp(b, sizeof(b), p, l, s, t, PPPIPCP);
response = makeppp(b, sizeof(b), p, l, s, t, PPPIPCP, 0, 0, 0);
if (!response) return;
*response = ConfigAck;
}
@ -1119,7 +1331,7 @@ void processipcp(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l)
switch (session[s].ppp.ipcp)
{
case Closed:
response = makeppp(b, sizeof(b), p, 2, s, t, PPPIPCP);
response = makeppp(b, sizeof(b), p, 2, s, t, PPPIPCP, 0, 0, 0);
if (!response) return;
*response = TerminateAck;
*((uint16_t *) (response + 2)) = htons(l = 4);
@ -1192,7 +1404,7 @@ void processipcp(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l)
}
*p = TerminateAck; // send ack
q = makeppp(b, sizeof(b), p, l, s, t, PPPIPCP);
q = makeppp(b, sizeof(b), p, l, s, t, PPPIPCP, 0, 0, 0);
if (!q) return;
LOG(3, s, t, "IPCP: send %s\n", ppp_code(*q));
@ -1326,7 +1538,7 @@ void processipv6cp(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l)
else if (gotip)
{
// Send packet back as ConfigAck
response = makeppp(b, sizeof(b), p, l, s, t, PPPIPV6CP);
response = makeppp(b, sizeof(b), p, l, s, t, PPPIPV6CP, 0, 0, 0);
if (!response) return;
*response = ConfigAck;
}
@ -1340,7 +1552,7 @@ void processipv6cp(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l)
switch (session[s].ppp.ipv6cp)
{
case Closed:
response = makeppp(b, sizeof(b), p, 2, s, t, PPPIPV6CP);
response = makeppp(b, sizeof(b), p, 2, s, t, PPPIPV6CP, 0, 0, 0);
if (!response) return;
*response = TerminateAck;
*((uint16_t *) (response + 2)) = htons(l = 4);
@ -1413,7 +1625,7 @@ void processipv6cp(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l)
}
*p = TerminateAck; // send ack
q = makeppp(b, sizeof(b), p, l, s, t, PPPIPV6CP);
q = makeppp(b, sizeof(b), p, l, s, t, PPPIPV6CP, 0, 0, 0);
if (!q) return;
LOG(3, s, t, "IPV6CP: send %s\n", ppp_code(*q));
@ -1456,12 +1668,15 @@ void processipin(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l)
if (session[s].ppp.phase != Network || session[s].ppp.ipcp != Opened)
return;
if (!session[s].bundle || bundle[session[s].bundle].num_of_links < 2) // FIXME:
{
// no spoof (do sessionbyip to handled statically routed subnets)
if (ip != session[s].ip && sessionbyip(htonl(ip)) != s)
{
LOG(5, s, t, "Dropping packet with spoofed IP %s\n", fmtaddr(htonl(ip), 0));
LOG(4, s, t, "Dropping packet with spoofed IP %s\n", fmtaddr(htonl(ip), 0));
return;
}
}
// run access-list if any
if (session[s].filter_in && !ip_filter(p, l, session[s].filter_in - 1))
@ -1526,6 +1741,167 @@ void processipin(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l)
INC_STAT(tun_tx_bytes, l);
}
// process Multilink PPP packet received
void processmpin(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l)
{
bundleidt b = session[s].bundle;
uint8_t begin_frame;
uint8_t end_frame;
uint32_t seq_num;
uint32_t offset;
if (!b)
{
LOG(3, s, t, "MPPP: Invalid bundle id: 0\n");
return;
}
begin_frame = (*p & 0x80);
end_frame = (*p & 0x40);
if (session[s].mssf)
{
// Get 12 bit for seq number
uint16_t short_seq_num = ntohs((*(uint16_t *) p) & 0xFF0F);
uint16_t short_seq_num2 = short_seq_num >> 4;
p += 2;
l -= 2;
seq_num = short_seq_num2;
// After this point the pointer should be advanced 2 bytes
LOG(3, s, t, "MPPP: 12 bits, sequence number: %d, short1: %d, short2: %d\n",seq_num, short_seq_num, short_seq_num2);
}
else
{
// Get 24 bit for seq number
p++;
seq_num = ntohl((*(uint32_t *) p) & 0xFFFFFF00);
seq_num = seq_num >> 8;
p += 3;
l -= 4;
// After this point the pointer should be advanced 4 bytes
LOG(4, s, t, "MPPP: 24 bits sequence number:%d\n",seq_num);
}
if (seq_num - bundle[b].offset < 0)
{
bundle[b].offset = 0;
bundle[b].pending_frag = 0;
}
offset = bundle[b].offset;
if (begin_frame)
{
// Check for previous non-assembled frames
int error = 0;
if (bundle[b].pending_frag)
{
uint32_t fn = bundle[b].seq_num_m - offset;
uint16_t cur_len;
bundle[b].pending_frag = 0;
// Check for array indexes
if (fn < 0 || fn > MAXFRAGNUM)
{
LOG(2, s, t, "ERROR: Index out of range fn:%d, bundle:%d\n",fn,b);
return;
}
if (seq_num-offset < 0 || seq_num-offset > MAXFRAGNUM)
{
LOG(2, s, t, "ERROR: Index out of range fn(last):%d, bundle:%d\n",fn,b);
return;
}
/////////////////////////////////////////////////////
cur_len = 4; // This is set to 4 to leave 4 bytes for function processipin
for (fn = bundle[b].seq_num_m - offset; fn < seq_num - offset; fn++)
{
if (!frag[b].fragment[fn].length)
{
LOG(4, s, t, "MPPP: Found lost fragment while reassembling frame %d in (%d,%d)\n",fn, bundle[b].seq_num_m-offset, seq_num-offset);
error = 1;
break;
}
if (cur_len + frag[b].fragment[fn].length > MAXETHER)
{
LOG(2, s, t, "MPPP: ERROR: very long frame after assembling %d\n", frag[b].fragment[fn].length+cur_len);
error = 1;
break;
}
memcpy(frag[b].reassembled_frame+cur_len, frag[b].fragment[fn].data, frag[b].fragment[fn].length);
cur_len += frag[b].fragment[fn].length;
frag[b].fragment[fn].length = 0; // Indicates that this fragment has been consumed
// This is usefull for compression
memset(frag[b].fragment[fn].data, 0, sizeof(frag[b].fragment[fn].data));
}
if (!error)
{
frag[b].re_frame_len = cur_len;
// Process the resassembled frame
LOG(4, s, t, "MPPP: Process the reassembled frame, len=%d\n",cur_len);
processmpframe(s, t, frag[b].reassembled_frame, frag[b].re_frame_len, 1);
// Set reassembled frame length to zero after processing it
frag[b].re_frame_len = 0;
memset(frag[b].reassembled_frame, 0, sizeof(frag[b].reassembled_frame));
}
}
//////////////////////////////////////////
bundle[b].seq_num_m = seq_num;
if (end_frame)
{
// Both bits are set
LOG(4, s, t, "MPPP: Both bits are set (Begin and End).\n");
processmpframe(s, t, p, l, 0);
// The maximum number of fragments is 1500
if (seq_num - bundle[b].offset >= 1400)
{
bundle[b].offset = seq_num;
LOG(4, s, t, "MPPP: Setting offset to: %d\n",bundle[b].offset);
}
}
else
{
bundle[b].pending_frag = 1;
// End bit is clear
LOG(4, s, t, "MPPP: Push to receive buffer\n");
// Push to the receive buffer
// Array indexes checking
if (seq_num-offset < 0 || seq_num-offset >= MAXFRAGNUM)
{
LOG(2, s, t, "ERROR: Index out of range, push to receive buffer(1) seq:%d, offset:%d, bundle:%d\n",seq_num,offset,b);
return;
}
// Perform length checking
if (l > MAXFRAGLEN)
{
LOG(2, s, t, "MPPP: ERROR: very long fragment length (1)\n");
return;
}
frag[b].fragment[seq_num - offset].length = l;
memcpy(frag[b].fragment[seq_num - offset].data, p, l);
}
}
else
{
LOG(4, s, t, "MPPP: Push to receive buffer\n");
// Push to the receive buffer
// Array indexes checking
if (seq_num-offset < 0 || seq_num-offset >= MAXFRAGNUM)
{
LOG(2, s, t, "ERROR: Index out of range, push to receive buffer(2) seq:%d, offset:%d, bundle:%d\n",seq_num,offset,b);
return;
}
// Perform length checking
if (l > MAXFRAGLEN)
{
LOG(2, s, t, "MPPP: ERROR: very long fragment length (2).\n");
return;
}
frag[b].fragment[seq_num - offset].length = l;
memcpy(frag[b].fragment[seq_num - offset].data, p, l);
}
}
// process IPv6 packet received
//
// This MUST be called with at least 4 byte behind 'p'.
@ -1685,7 +2061,7 @@ void processccp(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l)
STAT(tunnel_rx_errors);
}
LOG(3, s, t, "CCP: recv %s\n", ppp_code(*p));
LOG(4, s, t, "CCP: recv %s\n", ppp_code(*p));
if (*p == ConfigAck)
{
switch (session[s].ppp.ccp)
@ -1718,13 +2094,13 @@ void processccp(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l)
else // compression requested--reject
*p = ConfigRej;
q = makeppp(b, sizeof(b), p, l, s, t, PPPCCP);
q = makeppp(b, sizeof(b), p, l, s, t, PPPCCP, 0, 0, 0);
if (!q) return;
switch (session[s].ppp.ccp)
{
case Closed:
q = makeppp(b, sizeof(b), p, 2, s, t, PPPCCP);
q = makeppp(b, sizeof(b), p, 2, s, t, PPPCCP, 0, 0, 0);
if (!q) return;
*q = TerminateAck;
*((uint16_t *) (q + 2)) = htons(l = 4);
@ -1770,13 +2146,13 @@ void processccp(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l)
return;
}
LOG(3, s, t, "CCP: send %s\n", ppp_code(*q));
LOG(4, s, t, "CCP: send %s\n", ppp_code(*q));
tunnelsend(b, l + (q - b), t);
}
else if (*p == TerminateReq)
{
*p = TerminateAck;
q = makeppp(b, sizeof(b), p, l, s, t, PPPCCP);
q = makeppp(b, sizeof(b), p, l, s, t, PPPCCP, 0, 0, 0);
if (!q) return;
LOG(3, s, t, "CCP: send %s\n", ppp_code(*q));
tunnelsend(b, l + (q - b), t);
@ -1820,7 +2196,7 @@ void sendchap(sessionidt s, tunnelidt t)
STAT(tunnel_tx_errors);
return ;
}
q = makeppp(b, sizeof(b), 0, 0, s, t, PPPCHAP);
q = makeppp(b, sizeof(b), 0, 0, s, t, PPPCHAP, 0, 0, 0);
if (!q) return;
*q = 1; // challenge
@ -1834,37 +2210,86 @@ void sendchap(sessionidt s, tunnelidt t)
// fill in a L2TP message with a PPP frame,
// returns start of PPP frame
uint8_t *makeppp(uint8_t *b, int size, uint8_t *p, int l, sessionidt s, tunnelidt t, uint16_t mtype)
uint8_t *makeppp(uint8_t *b, int size, uint8_t *p, int l, sessionidt s, tunnelidt t, uint16_t mtype, uint8_t prio, bundleidt bid, uint8_t mp_bits)
{
if (size < 12) // Need more space than this!!
uint16_t hdr = 0x0002; // L2TP with no options
uint16_t type = mtype;
uint8_t *start = b;
if (size < 16) // Need more space than this!!
{
LOG(0, s, t, "makeppp buffer too small for L2TP header (size=%d)\n", size);
return NULL;
}
*(uint16_t *) (b + 0) = htons(0x0002); // L2TP with no options
if (prio) hdr |= 0x0100; // set priority bit
*(uint16_t *) (b + 0) = htons(hdr);
*(uint16_t *) (b + 2) = htons(tunnel[t].far); // tunnel
*(uint16_t *) (b + 4) = htons(session[s].far); // session
b += 6;
if (mtype == PPPLCP || !(session[s].flags & SESSION_ACFC))
// Check whether this session is part of multilink
if (bid)
{
if (bundle[bid].num_of_links > 1)
type = PPPMP; // Change PPP message type to the PPPMP
else
bid = 0;
}
if (type == PPPLCP || !(session[s].flags & SESSION_ACFC))
{
*(uint16_t *) b = htons(0xFF03); // HDLC header
b += 2;
}
if (mtype < 0x100 && session[s].flags & SESSION_PFC)
*b++ = mtype;
if (type < 0x100 && session[s].flags & SESSION_PFC)
{
*b++ = type;
}
else
{
*(uint16_t *) b = htons(mtype);
*(uint16_t *) b = htons(type);
b += 2;
}
if (l + 12 > size)
if (bid)
{
LOG(2, s, t, "makeppp would overflow buffer (size=%d, header+payload=%d)\n", size, l + 12);
// Set the sequence number and (B)egin (E)nd flags
if (session[s].mssf)
{
// Set the multilink bits
uint16_t bits_send = mp_bits;
*(uint16_t *) b = htons((bundle[bid].seq_num_t & 0xFF0F)|bits_send);
b += 2;
}
else
{
*(uint32_t *) b = htonl(bundle[bid].seq_num_t);
// Set the multilink bits
*b = mp_bits;
b += 4;
}
bundle[bid].seq_num_t++;
// Add the message type if this fragment has the begin bit set
if (mp_bits & MP_BEGIN)
{
//*b++ = mtype; // The next two lines are instead of this
*(uint16_t *) b = htons(mtype); // Message type
b += 2;
}
}
if ((b - start) + l > size)
{
LOG(2, s, t, "makeppp would overflow buffer (size=%d, header+payload=%d)\n", size, (b - start) + l);
return NULL;
}
// Copy the payload
if (p && l)
memcpy(b, p, l);
@ -1903,10 +2328,10 @@ void sendlcp(sessionidt s, tunnelidt t)
uint8_t b[500], *q, *l;
int authtype = sess_local[s].lcp_authtype;
if (!(q = makeppp(b, sizeof(b), NULL, 0, s, t, PPPLCP)))
if (!(q = makeppp(b, sizeof(b), NULL, 0, s, t, PPPLCP, 0, 0, 0)))
return;
LOG(3, s, t, "LCP: send ConfigReq%s%s%s\n",
LOG(3, s, t, "LCP: send ConfigReq%s%s%s including MP options\n",
authtype ? " (" : "",
authtype ? (authtype == AUTHCHAP ? "CHAP" : "PAP") : "",
authtype ? ")" : "");
@ -1933,6 +2358,20 @@ void sendlcp(sessionidt s, tunnelidt t)
l += 4;
}
if (sess_local[s].mp_mrru)
{
*l++ = 17; *l++ = 4; // Multilink Max-Receive-Reconstructed-Unit (length 4)
*(uint16_t *) l = htons(sess_local[s].mp_mrru); l += 2;
}
if (sess_local[s].mp_epdis)
{
*l++ = 19; *l++ = 7; // Multilink Endpoint Discriminator (length 7)
*l++ = IPADDR; // Endpoint Discriminator class
*(uint32_t *) l = htonl(sess_local[s].mp_epdis);
l += 4;
}
*(uint16_t *)(q + 2) = htons(l - q); // Length
LOG_HEX(5, "PPPLCP", q, l - q);
@ -1947,7 +2386,7 @@ void sendccp(sessionidt s, tunnelidt t)
{
uint8_t b[500], *q;
if (!(q = makeppp(b, sizeof(b), NULL, 0, s, t, PPPCCP)))
if (!(q = makeppp(b, sizeof(b), NULL, 0, s, t, PPPCCP, 0, 0, 0)))
return;
LOG(3, s, t, "CCP: send ConfigReq (no compression)\n");
@ -1974,7 +2413,7 @@ void protoreject(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l, uint16_t pro
l += 6;
if (l > mru) l = mru;
q = makeppp(buf, sizeof(buf), 0, 0, s, t, PPPLCP);
q = makeppp(buf, sizeof(buf), 0, 0, s, t, PPPLCP, 0, 0, 0);
if (!q) return;
*q = ProtocolRej;

View file

@ -1,6 +1,6 @@
// L2TPNS Radius Stuff
char const *cvs_id_radius = "$Id: radius.c,v 1.49 2006/04/13 11:14:35 bodea Exp $";
char const *cvs_id_radius = "$Id: radius.c,v 1.50 2006/04/27 09:53:50 bodea Exp $";
#include <time.h>
#include <stdio.h>
@ -537,7 +537,7 @@ void processrad(uint8_t *buf, int len, char socket_index)
if (radius[r].chap)
{
// CHAP
uint8_t *p = makeppp(b, sizeof(b), 0, 0, s, t, PPPCHAP);
uint8_t *p = makeppp(b, sizeof(b), 0, 0, s, t, PPPCHAP, 0, 0, 0);
if (!p) return; // Abort!
*p = (r_code == AccessAccept) ? 3 : 4; // ack/nak
@ -551,7 +551,7 @@ void processrad(uint8_t *buf, int len, char socket_index)
else
{
// PAP
uint8_t *p = makeppp(b, sizeof(b), 0, 0, s, t, PPPPAP);
uint8_t *p = makeppp(b, sizeof(b), 0, 0, s, t, PPPPAP, 0, 0, 0);
if (!p) return; // Abort!
// ack/nak
@ -593,6 +593,19 @@ void processrad(uint8_t *buf, int len, char socket_index)
LOG(3, s, session[s].tunnel, " Radius reply contains primary DNS address %s\n",
fmtaddr(htonl(session[s].dns1), 0));
}
else if (*p == 27)
{
// Session timeout
if (p[1] < 6) {
LOG(2, s, session[s].tunnel, "Error: Received Session timeout with length %d < 6\n", p[1]);
continue;
}
session[s].timeout = ntohl(*(uint32_t *) (p + 2));
LOG(3, s, session[s].tunnel, " Radius reply contains Session timeout %d\n", session[s].timeout);
if (!session[s].timeout)
sessionshutdown(s, "Session timeout is zero", CDN_ADMIN_DISC, TERM_SESSION_TIMEOUT);
}
else if (*p == 136)
{
// DNS address