To support proxy LCP negotiation. Note: we *have* to take the auth id from the proxy answer, otherwise we would replay previous ids, for which the client might cache the answer and thus ignore our new challenge and just repeat their outdated answer.
3188 lines
79 KiB
C
3188 lines
79 KiB
C
// L2TPNS PPP Stuff
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <errno.h>
|
|
#include <stdlib.h>
|
|
#include <netinet/ip6.h>
|
|
#include "dhcp6.h"
|
|
#include "l2tpns.h"
|
|
#include "constants.h"
|
|
#include "plugin.h"
|
|
#include "util.h"
|
|
#include "tbf.h"
|
|
#include "cluster.h"
|
|
|
|
#include "l2tplac.h"
|
|
#include "pppoe.h"
|
|
|
|
extern tunnelt *tunnel;
|
|
extern bundlet *bundle;
|
|
extern fragmentationt *frag;
|
|
extern sessiont *session;
|
|
extern radiust *radius;
|
|
extern int tunfd;
|
|
extern char hostname[];
|
|
extern uint32_t eth_tx;
|
|
extern time_t time_now;
|
|
extern uint64_t time_now_ms;
|
|
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);
|
|
|
|
static int first_session_in_bundle(sessionidt s)
|
|
{
|
|
bundleidt i;
|
|
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))
|
|
return 0;
|
|
return 1;
|
|
}
|
|
|
|
// Process PAP messages
|
|
void processpap(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l)
|
|
{
|
|
char user[MAXUSER];
|
|
char pass[MAXPASS];
|
|
uint16_t hl;
|
|
uint16_t r;
|
|
|
|
CSTAT(processpap);
|
|
|
|
LOG_HEX(5, "PAP", p, l);
|
|
if (l < 4)
|
|
{
|
|
LOG(1, s, t, "Short PAP %u bytes\n", l);
|
|
STAT(tunnel_rx_errors);
|
|
sessionshutdown(s, "Short PAP packet.", CDN_ADMIN_DISC, TERM_USER_ERROR);
|
|
return;
|
|
}
|
|
|
|
if ((hl = ntohs(*(uint16_t *) (p + 2))) > l)
|
|
{
|
|
LOG(1, s, t, "Length mismatch PAP %u/%u\n", hl, l);
|
|
STAT(tunnel_rx_errors);
|
|
sessionshutdown(s, "PAP length mismatch.", CDN_ADMIN_DISC, TERM_USER_ERROR);
|
|
return;
|
|
}
|
|
l = hl;
|
|
|
|
if (*p != 1)
|
|
{
|
|
LOG(1, s, t, "Unexpected PAP code %d\n", *p);
|
|
STAT(tunnel_rx_errors);
|
|
sessionshutdown(s, "Unexpected PAP code.", CDN_ADMIN_DISC, TERM_USER_ERROR);
|
|
return;
|
|
}
|
|
|
|
if (session[s].ppp.phase != Authenticate)
|
|
{
|
|
LOG(2, s, t, "PAP ignored in %s phase\n", ppp_phase(session[s].ppp.phase));
|
|
return;
|
|
}
|
|
|
|
{
|
|
uint8_t *b = p;
|
|
b += 4;
|
|
user[0] = pass[0] = 0;
|
|
if (*b && *b < sizeof(user))
|
|
{
|
|
memcpy(user, b + 1, *b);
|
|
user[*b] = 0;
|
|
b += 1 + *b;
|
|
if (*b && *b < sizeof(pass))
|
|
{
|
|
memcpy(pass, b + 1, *b);
|
|
pass[*b] = 0;
|
|
}
|
|
}
|
|
LOG(3, s, t, "PAP login %s/%s\n", user, pass);
|
|
}
|
|
|
|
if ((!config->disable_lac_func) && lac_conf_forwardtoremotelns(s, user))
|
|
{
|
|
// Creating a tunnel/session has been started
|
|
return;
|
|
}
|
|
|
|
if (session[s].ip || !(r = radiusnew(s)))
|
|
{
|
|
// 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, 0, 0, 0);
|
|
if (!p) return;
|
|
|
|
if (session[s].ip)
|
|
*p = 2; // ACK
|
|
else
|
|
*p = 3; // cant authorise
|
|
p[1] = id;
|
|
*(uint16_t *) (p + 2) = htons(5); // length
|
|
p[4] = 0; // no message
|
|
tunnelsend(b, 5 + (p - b), t); // send it
|
|
|
|
if (session[s].ip)
|
|
{
|
|
LOG(3, s, t, "Already an IP allocated: %s (%d)\n",
|
|
fmtaddr(htonl(session[s].ip), 0), session[s].ip_pool_index);
|
|
}
|
|
else
|
|
{
|
|
LOG(1, s, t, "No RADIUS session available to authenticate session...\n");
|
|
sessionshutdown(s, "No free RADIUS sessions.", CDN_UNAVAILABLE, TERM_SERVICE_UNAVAILABLE);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Run PRE_AUTH plugins
|
|
struct param_pre_auth packet = { &tunnel[t], &session[s], strdup(user), strdup(pass), PPPPAP, 1 };
|
|
run_plugins(PLUGIN_PRE_AUTH, &packet);
|
|
if (!packet.continue_auth)
|
|
{
|
|
LOG(3, s, t, "A plugin rejected PRE_AUTH\n");
|
|
if (packet.username) free(packet.username);
|
|
if (packet.password) free(packet.password);
|
|
return;
|
|
}
|
|
|
|
strncpy(session[s].user, packet.username, sizeof(session[s].user) - 1);
|
|
strncpy(radius[r].pass, packet.password, sizeof(radius[r].pass) - 1);
|
|
|
|
strncpy((char *) sess_local[s].auth_name, packet.username, sizeof(sess_local[s].auth_name) - 1);
|
|
strncpy((char *) sess_local[s].auth_resp, packet.password, sizeof(sess_local[s].auth_resp) - 1);
|
|
|
|
free(packet.username);
|
|
free(packet.password);
|
|
|
|
radius[r].id = sess_local[s].auth_id = p[1]; // Take ID from client
|
|
LOG(3, s, t, "Sending login for %s/%s to RADIUS\n", user, pass);
|
|
if ((session[s].mrru) && (!first_session_in_bundle(s)))
|
|
radiussend(r, RADIUSJUSTAUTH);
|
|
else
|
|
radiussend(r, RADIUSAUTH);
|
|
}
|
|
}
|
|
|
|
// Re-send a CHAP ack, in case it was previously lost
|
|
void resendchapack(sessionidt s, tunnelidt t, uint16_t auth_id)
|
|
{
|
|
uint8_t b[MAXETHER];
|
|
uint8_t *p;
|
|
p = makeppp(b, sizeof(b), 0, 0, s, t, PPPCHAP, 0, 0, 0);
|
|
if (!p) return;
|
|
*p = 3; // ack
|
|
p[1] = auth_id;
|
|
*(uint16_t *) (p + 2) = ntohs(4); // no message
|
|
tunnelsend(b, (p - b) + 4, t); // send it
|
|
}
|
|
|
|
// Process CHAP messages
|
|
void processchap(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l)
|
|
{
|
|
uint16_t r;
|
|
uint16_t hl;
|
|
|
|
CSTAT(processchap);
|
|
|
|
LOG_HEX(5, "CHAP", p, l);
|
|
|
|
if (l < 4)
|
|
{
|
|
LOG(1, s, t, "Short CHAP %u bytes\n", l);
|
|
STAT(tunnel_rx_errors);
|
|
sessionshutdown(s, "Short CHAP packet.", CDN_ADMIN_DISC, TERM_USER_ERROR);
|
|
return;
|
|
}
|
|
|
|
if ((hl = ntohs(*(uint16_t *) (p + 2))) > l)
|
|
{
|
|
LOG(1, s, t, "Length mismatch CHAP %u/%u\n", hl, l);
|
|
STAT(tunnel_rx_errors);
|
|
sessionshutdown(s, "CHAP length mismatch.", CDN_ADMIN_DISC, TERM_USER_ERROR);
|
|
return;
|
|
}
|
|
l = hl;
|
|
|
|
if (*p != 2)
|
|
{
|
|
LOG(1, s, t, "Unexpected CHAP response code %d\n", *p);
|
|
STAT(tunnel_rx_errors);
|
|
sessionshutdown(s, "CHAP length mismatch.", CDN_ADMIN_DISC, TERM_USER_ERROR);
|
|
return;
|
|
}
|
|
|
|
if (session[s].ppp.phase != Authenticate)
|
|
{
|
|
if (session[s].ppp.phase == Network)
|
|
{
|
|
LOG(2, s, t, "CHAP in Network phase, peer probably missed our ack, confirming it\n");
|
|
resendchapack(s, t, p[1]);
|
|
}
|
|
else
|
|
LOG(2, s, t, "CHAP ignored in %s phase\n", ppp_phase(session[s].ppp.phase));
|
|
return;
|
|
}
|
|
|
|
r = sess_local[s].radius;
|
|
if (!r)
|
|
{
|
|
LOG(3, s, t, "Unexpected CHAP message\n");
|
|
|
|
// Some modems (Netgear DM602, possibly others) persist in using CHAP even
|
|
// after ACKing our ConfigReq for PAP.
|
|
if (sess_local[s].lcp_authtype == AUTHPAP && config->radius_authtypes & AUTHCHAP)
|
|
{
|
|
sess_local[s].lcp_authtype = AUTHCHAP;
|
|
sendchap(s, t);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (p[1] != radius[r].id)
|
|
{
|
|
LOG(1, s, t, "Wrong CHAP response ID %d (should be %d) (%d)\n", p[1], radius[r].id, r);
|
|
STAT(tunnel_rx_errors);
|
|
sessionshutdown(s, "Unexpected CHAP response ID.", CDN_ADMIN_DISC, TERM_USER_ERROR);
|
|
return;
|
|
}
|
|
|
|
if (l < 5 || p[4] != 16)
|
|
{
|
|
LOG(1, s, t, "Bad CHAP response length %d\n", l < 5 ? -1 : p[4]);
|
|
STAT(tunnel_rx_errors);
|
|
sessionshutdown(s, "Bad CHAP response length.", CDN_ADMIN_DISC, TERM_USER_ERROR);
|
|
return;
|
|
}
|
|
|
|
l -= 5;
|
|
p += 5;
|
|
if (l < 16 || l - 16 >= sizeof(session[s].user))
|
|
{
|
|
LOG(1, s, t, "CHAP user too long %d\n", l - 16);
|
|
STAT(tunnel_rx_errors);
|
|
sessionshutdown(s, "CHAP username too long.", CDN_ADMIN_DISC, TERM_USER_ERROR);
|
|
return;
|
|
}
|
|
|
|
// Run PRE_AUTH plugins
|
|
{
|
|
struct param_pre_auth packet = { &tunnel[t], &session[s], NULL, NULL, PPPCHAP, 1 };
|
|
|
|
packet.password = calloc(17, 1);
|
|
memcpy(packet.password, p, 16);
|
|
|
|
p += 16;
|
|
l -= 16;
|
|
|
|
packet.username = calloc(l + 1, 1);
|
|
memcpy(packet.username, p, l);
|
|
|
|
if ((!config->disable_lac_func) && lac_conf_forwardtoremotelns(s, packet.username))
|
|
{
|
|
free(packet.username);
|
|
free(packet.password);
|
|
// Creating a tunnel/session has been started
|
|
return;
|
|
}
|
|
|
|
run_plugins(PLUGIN_PRE_AUTH, &packet);
|
|
if (!packet.continue_auth)
|
|
{
|
|
LOG(3, s, t, "A plugin rejected PRE_AUTH\n");
|
|
if (packet.username) free(packet.username);
|
|
if (packet.password) free(packet.password);
|
|
return;
|
|
}
|
|
|
|
strncpy(session[s].user, packet.username, sizeof(session[s].user) - 1);
|
|
memcpy(radius[r].pass, packet.password, 16);
|
|
|
|
strncpy((char *) sess_local[s].auth_name, packet.username, sizeof(sess_local[s].auth_name) - 1);
|
|
memcpy(sess_local[s].auth_resp, packet.password, 16);
|
|
|
|
free(packet.username);
|
|
free(packet.password);
|
|
}
|
|
|
|
radius[r].chap = 1;
|
|
LOG(3, s, t, "CHAP login %s\n", session[s].user);
|
|
if ((session[s].mrru) && (!first_session_in_bundle(s)))
|
|
radiussend(r, RADIUSJUSTAUTH);
|
|
else
|
|
radiussend(r, RADIUSAUTH);
|
|
}
|
|
|
|
static void dumplcp(uint8_t *p, int l)
|
|
{
|
|
int x = l - 4;
|
|
uint8_t *o = (p + 4);
|
|
|
|
LOG_HEX(5, "PPP LCP Packet", p, l);
|
|
LOG(4, 0, 0, "PPP LCP Packet type %d (%s len %d)\n", *p, ppp_code((int)*p), ntohs( ((uint16_t *) p)[1]) );
|
|
LOG(4, 0, 0, "Length: %d\n", l);
|
|
if (*p != ConfigReq && *p != ConfigRej && *p != ConfigAck)
|
|
return;
|
|
|
|
while (x > 2)
|
|
{
|
|
int type = o[0];
|
|
int length = o[1];
|
|
if (length < 2)
|
|
{
|
|
LOG(4, 0, 0, " Option length is %d...\n", length);
|
|
break;
|
|
}
|
|
if (type == 0)
|
|
{
|
|
LOG(4, 0, 0, " Option type is 0...\n");
|
|
x -= length;
|
|
o += length;
|
|
continue;
|
|
}
|
|
switch (type)
|
|
{
|
|
case 1: // Maximum-Receive-Unit
|
|
if (length == 4)
|
|
LOG(4, 0, 0, " %s %d\n", ppp_lcp_option(type), ntohs(*(uint16_t *)(o + 2)));
|
|
else
|
|
LOG(4, 0, 0, " %s odd length %d\n", ppp_lcp_option(type), length);
|
|
break;
|
|
case 2: // Async-Control-Character-Map
|
|
if (length == 6)
|
|
{
|
|
uint32_t asyncmap = ntohl(*(uint32_t *)(o + 2));
|
|
LOG(4, 0, 0, " %s %x\n", ppp_lcp_option(type), asyncmap);
|
|
}
|
|
else
|
|
LOG(4, 0, 0, " %s odd length %d\n", ppp_lcp_option(type), length);
|
|
break;
|
|
case 3: // Authentication-Protocol
|
|
if (length == 4)
|
|
{
|
|
int proto = ntohs(*(uint16_t *)(o + 2));
|
|
LOG(4, 0, 0, " %s 0x%x (%s)\n", ppp_lcp_option(type), proto,
|
|
proto == PPPPAP ? "PAP" : "UNSUPPORTED");
|
|
}
|
|
else if (length == 5)
|
|
{
|
|
int proto = ntohs(*(uint16_t *)(o + 2));
|
|
int algo = *(o + 4);
|
|
LOG(4, 0, 0, " %s 0x%x 0x%x (%s)\n", ppp_lcp_option(type), proto, algo,
|
|
(proto == PPPCHAP && algo == 5) ? "CHAP MD5" : "UNSUPPORTED");
|
|
}
|
|
else
|
|
LOG(4, 0, 0, " %s odd length %d\n", ppp_lcp_option(type), length);
|
|
break;
|
|
case 4: // Quality-Protocol
|
|
{
|
|
uint32_t qp = ntohl(*(uint32_t *)(o + 2));
|
|
LOG(4, 0, 0, " %s %x\n", ppp_lcp_option(type), qp);
|
|
}
|
|
break;
|
|
case 5: // Magic-Number
|
|
if (length == 6)
|
|
{
|
|
uint32_t magicno = ntohl(*(uint32_t *)(o + 2));
|
|
LOG(4, 0, 0, " %s %x\n", ppp_lcp_option(type), magicno);
|
|
}
|
|
else
|
|
LOG(4, 0, 0, " %s odd length %d\n", ppp_lcp_option(type), length);
|
|
break;
|
|
case 7: // Protocol-Field-Compression
|
|
case 8: // Address-And-Control-Field-Compression
|
|
LOG(4, 0, 0, " %s\n", ppp_lcp_option(type));
|
|
break;
|
|
default:
|
|
LOG(2, 0, 0, " Unknown PPP LCP Option type %d\n", type);
|
|
break;
|
|
}
|
|
x -= length;
|
|
o += length;
|
|
}
|
|
}
|
|
|
|
void lcp_open(sessionidt s, tunnelidt t)
|
|
{
|
|
// transition to Authentication or Network phase:
|
|
session[s].ppp.phase = sess_local[s].lcp_authtype && !sess_local[s].lcp_authdone ? Authenticate : Network;
|
|
|
|
LOG(3, s, t, "LCP: Opened, phase %s\n", ppp_phase(session[s].ppp.phase));
|
|
|
|
// LCP now Opened
|
|
change_state(s, lcp, Opened);
|
|
|
|
if (session[s].ppp.phase == Authenticate)
|
|
{
|
|
if (sess_local[s].lcp_authtype == AUTHCHAP)
|
|
sendchap(s, t);
|
|
}
|
|
else
|
|
{
|
|
if(session[s].bundle == 0 || bundle[session[s].bundle].num_of_links == 1)
|
|
{
|
|
// This-Layer-Up
|
|
sendipcp(s, t);
|
|
change_state(s, ipcp, RequestSent);
|
|
// move to passive state for IPv6 (if configured), CCP
|
|
if (config->ipv6_prefix.s6_addr[0])
|
|
change_state(s, ipv6cp, Stopped);
|
|
else
|
|
change_state(s, ipv6cp, Closed);
|
|
|
|
change_state(s, ccp, Stopped);
|
|
}
|
|
else
|
|
{
|
|
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);
|
|
ipcp_open(s, t);
|
|
}
|
|
}
|
|
}
|
|
|
|
void lcp_restart(sessionidt s)
|
|
{
|
|
session[s].ppp.phase = Establish;
|
|
sess_local[s].lcp_last_received_confreq_n = 0;
|
|
sess_local[s].lcp_last_sent_confreq_n = 0;
|
|
sess_local[s].auth_type = 0;
|
|
sess_local[s].auth_name[0] = 0;
|
|
// This-Layer-Down
|
|
change_state(s, ipcp, Initial);
|
|
change_state(s, ipv6cp, Initial);
|
|
change_state(s, ccp, Initial);
|
|
}
|
|
|
|
static uint8_t *ppp_conf_rej(sessionidt s, uint8_t *buf, size_t blen, uint16_t mtype,
|
|
uint8_t **response, uint8_t *queued, uint8_t *packet, uint8_t *option)
|
|
{
|
|
if (!*response || **response != ConfigRej)
|
|
{
|
|
queued = *response = makeppp(buf, blen, packet, 2, s, session[s].tunnel, mtype, 0, 0, 0);
|
|
if (!queued)
|
|
return 0;
|
|
|
|
*queued = ConfigRej;
|
|
queued += 4;
|
|
}
|
|
|
|
if ((queued - buf + option[1]) > blen)
|
|
{
|
|
LOG(2, s, session[s].tunnel, "PPP overflow for ConfigRej (proto %u, option %u).\n", mtype, *option);
|
|
return 0;
|
|
}
|
|
|
|
memcpy(queued, option, option[1]);
|
|
return queued + option[1];
|
|
}
|
|
|
|
static uint8_t *ppp_conf_nak(sessionidt s, uint8_t *buf, size_t blen, uint16_t mtype,
|
|
uint8_t **response, uint8_t *queued, uint8_t *packet, uint8_t *option,
|
|
uint8_t *value, size_t vlen)
|
|
{
|
|
int *nak_sent;
|
|
switch (mtype)
|
|
{
|
|
case PPPLCP: nak_sent = &sess_local[s].lcp.nak_sent; break;
|
|
case PPPIPCP: nak_sent = &sess_local[s].ipcp.nak_sent; break;
|
|
case PPPIPV6CP: nak_sent = &sess_local[s].ipv6cp.nak_sent; break;
|
|
default: return 0; // ?
|
|
}
|
|
|
|
if (*response && **response != ConfigNak)
|
|
{
|
|
if (*nak_sent < config->ppp_max_failure) // reject queued
|
|
return queued;
|
|
|
|
return ppp_conf_rej(s, buf, blen, mtype, response, 0, packet, option);
|
|
}
|
|
|
|
if (!*response)
|
|
{
|
|
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, 0, 0, 0);
|
|
if (!queued)
|
|
return 0;
|
|
|
|
(*nak_sent)++;
|
|
*queued = ConfigNak;
|
|
queued += 4;
|
|
}
|
|
|
|
if ((queued - buf + vlen + 2) > blen)
|
|
{
|
|
LOG(2, s, session[s].tunnel, "PPP overflow for ConfigNak (proto %u, option %u).\n", mtype, *option);
|
|
return 0;
|
|
}
|
|
|
|
*queued++ = *option;
|
|
*queued++ = vlen + 2;
|
|
memcpy(queued, value, vlen);
|
|
return queued + vlen;
|
|
}
|
|
|
|
static void ppp_code_rej(sessionidt s, tunnelidt t, uint16_t proto,
|
|
char *pname, uint8_t *p, uint16_t l, uint8_t *buf, size_t size)
|
|
{
|
|
uint8_t *q;
|
|
int mru = session[s].mru;
|
|
if (mru < MINMTU) mru = MINMTU;
|
|
if (mru > size) mru = size;
|
|
|
|
l += 4;
|
|
if (l > mru) l = mru;
|
|
|
|
q = makeppp(buf, size, 0, 0, s, t, proto, 0, 0, 0);
|
|
if (!q) return;
|
|
|
|
*q = CodeRej;
|
|
*(q + 1) = ++sess_local[s].lcp_ident;
|
|
*(uint16_t *)(q + 2) = htons(l);
|
|
memcpy(q + 4, p, l - 4);
|
|
|
|
LOG(2, s, t, "Unexpected %s code %s\n", pname, ppp_code(*p));
|
|
LOG(3, s, t, "%s: send %s\n", pname, ppp_code(*q));
|
|
if (config->debug > 3) dumplcp(q, l);
|
|
|
|
tunnelsend(buf, l + (q - buf), t);
|
|
}
|
|
|
|
// Process received LCP options from o from packet p.
|
|
// Set changed to 1 if we changed configuration that should be sent to cluster slaves, fill response on nak or rej
|
|
static void processreceivedlcpconfreq(sessionidt s, tunnelidt t, uint8_t *p, uint8_t *o, uint16_t x, int *changed, uint8_t **response)
|
|
{
|
|
static uint8_t asyncmap[4] = { 0, 0, 0, 0 }; // all zero
|
|
static uint8_t authproto[5];
|
|
uint8_t b[MAXETHER];
|
|
uint8_t *q = NULL;
|
|
|
|
while (x > 2)
|
|
{
|
|
int type = o[0];
|
|
int length = o[1];
|
|
|
|
if (length == 0 || length == 1 || type == 0 || x < length) break;
|
|
switch (type)
|
|
{
|
|
case 1: // Maximum-Receive-Unit
|
|
{
|
|
uint16_t mru = ntohs(*(uint16_t *)(o + 2));
|
|
if (mru >= MINMTU)
|
|
{
|
|
session[s].mru = mru;
|
|
(*changed)++;
|
|
break;
|
|
}
|
|
|
|
LOG(3, s, t, " Remote requesting MRU of %u. Rejecting.\n", mru);
|
|
mru = htons(MRU);
|
|
q = ppp_conf_nak(s, b, sizeof(b), PPPLCP, response, q, p, o, (uint8_t *) &mru, sizeof(mru));
|
|
}
|
|
break;
|
|
|
|
case 2: // Async-Control-Character-Map
|
|
if (!ntohl(*(uint32_t *)(o + 2))) // all bits zero is OK
|
|
break;
|
|
|
|
LOG(3, s, t, " Remote requesting asyncmap. Rejecting.\n");
|
|
q = ppp_conf_nak(s, b, sizeof(b), PPPLCP, response, q, p, o, asyncmap, sizeof(asyncmap));
|
|
break;
|
|
|
|
case 3: // Authentication-Protocol
|
|
{
|
|
int proto = ntohs(*(uint16_t *)(o + 2));
|
|
char proto_name[] = "0x0000";
|
|
int alen;
|
|
|
|
if (proto == PPPPAP)
|
|
{
|
|
if (config->radius_authtypes & AUTHPAP)
|
|
{
|
|
sess_local[s].lcp_authtype = AUTHPAP;
|
|
break;
|
|
}
|
|
|
|
strcpy(proto_name, "PAP");
|
|
}
|
|
else if (proto == PPPCHAP)
|
|
{
|
|
if (config->radius_authtypes & AUTHCHAP
|
|
&& *(o + 4) == 5) // MD5
|
|
{
|
|
sess_local[s].lcp_authtype = AUTHCHAP;
|
|
break;
|
|
}
|
|
|
|
strcpy(proto_name, "CHAP");
|
|
}
|
|
else
|
|
sprintf(proto_name, "%#4.4x", proto);
|
|
|
|
LOG(3, s, t, " Remote requesting %s authentication. Rejecting.\n", proto_name);
|
|
|
|
alen = add_lcp_auth(authproto, sizeof(authproto), config->radius_authprefer);
|
|
if (alen < 2) break; // paranoia
|
|
|
|
q = ppp_conf_nak(s, b, sizeof(b), PPPLCP, response, q, p, o, authproto + 2, alen - 2);
|
|
if (q && **response == ConfigNak &&
|
|
config->radius_authtypes != config->radius_authprefer)
|
|
{
|
|
// alternate type
|
|
alen = add_lcp_auth(authproto, sizeof(authproto), config->radius_authtypes & ~config->radius_authprefer);
|
|
if (alen < 2) break;
|
|
q = ppp_conf_nak(s, b, sizeof(b), PPPLCP, response, q, p, o, authproto + 2, alen - 2);
|
|
}
|
|
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case 4: // Quality-Protocol
|
|
case 5: // Magic-Number
|
|
case 7: // Protocol-Field-Compression
|
|
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);
|
|
}
|
|
x -= length;
|
|
o += length;
|
|
}
|
|
}
|
|
|
|
// Process sent LCP options from o
|
|
// Set changed to 1 if we changed configuration that should be sent to cluster slaves
|
|
static int processsentlcpconfreq(sessionidt s, tunnelidt t, uint8_t *p, uint8_t *o, uint16_t x, int *changed)
|
|
{
|
|
int auth = 0;
|
|
int ok = 1;
|
|
|
|
while (x > 2)
|
|
{
|
|
int type = o[0];
|
|
int length = o[1];
|
|
|
|
if (length == 0 || length == 1 || type == 0 || x < length) break;
|
|
switch (type)
|
|
{
|
|
case 1: // Maximum-Receive-Unit
|
|
{
|
|
uint16_t mru = ntohs(*(uint16_t *)(o + 2));
|
|
if (mru >= MINMTU)
|
|
{
|
|
sess_local[s].ppp_mru = mru;
|
|
break;
|
|
}
|
|
|
|
ok = 0;
|
|
break;
|
|
}
|
|
|
|
case 2: // Async-Control-Character-Map
|
|
if (ntohl(*(uint32_t *)(o + 2)))
|
|
ok = 0;
|
|
break;
|
|
|
|
case 3: // Authentication-Protocol
|
|
{
|
|
int proto = ntohs(*(uint16_t *)(o + 2));
|
|
|
|
if (proto == PPPPAP)
|
|
{
|
|
if (config->radius_authtypes & AUTHPAP)
|
|
{
|
|
auth = sess_local[s].lcp_authtype = AUTHPAP;
|
|
break;
|
|
}
|
|
ok = 0;
|
|
}
|
|
else if (proto == PPPCHAP)
|
|
{
|
|
if (config->radius_authtypes & AUTHCHAP
|
|
&& *(o + 4) == 5) // MD5
|
|
{
|
|
auth = sess_local[s].lcp_authtype = AUTHCHAP;
|
|
break;
|
|
}
|
|
ok = 0;
|
|
}
|
|
else
|
|
ok = 0;
|
|
}
|
|
break;
|
|
|
|
case 4: // Quality-Protocol
|
|
break;
|
|
|
|
case 5: // Magic-Number
|
|
if (length < 6) break;
|
|
session[s].magic = ntohl(*(uint32_t *)(o + 2));
|
|
*changed = 1;
|
|
break;
|
|
|
|
case 7: // Protocol-Field-Compression
|
|
session[s].flags |= SESSION_PFC;
|
|
*changed = 1;
|
|
break;
|
|
case 8: // Address-And-Control-Field-Compression
|
|
session[s].flags |= SESSION_ACFC;
|
|
*changed = 1;
|
|
break;
|
|
|
|
case 17: // Multilink Max-Receive-Reconstructed-Unit
|
|
sess_local[s].mp_mrru = ntohs(*(uint16_t *)(o + 2));
|
|
break;
|
|
|
|
case 18: // Multilink Short Sequence Number Header Format
|
|
sess_local[s].mp_mssf = 1;
|
|
break;
|
|
|
|
case 19: // Multilink Endpoint Discriminator
|
|
ok = 0;
|
|
break;
|
|
|
|
default: // Reject any unknown options
|
|
ok = 0;
|
|
break;
|
|
}
|
|
x -= length;
|
|
o += length;
|
|
}
|
|
|
|
if (!ok)
|
|
// Return after taking note of magic and flags
|
|
return 0;
|
|
|
|
if (sess_local[s].lcp_authtype && !auth)
|
|
// We do want authentication, we have to check with client
|
|
return 0;
|
|
|
|
if (sess_local[s].mp_epdis)
|
|
// We do want Endpoint Discriminator
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
// Try to use LCP proxy to avoid renegotiating it (in case the LAC doesn't support passing LCP through)
|
|
int processlcpproxy(sessionidt s, tunnelidt t, uint8_t *sent_lcp, uint16_t sent_lcp_n, uint8_t *received_lcp, uint16_t received_lcp_n)
|
|
{
|
|
uint8_t *response = 0;
|
|
int changed = 0;
|
|
|
|
if (!strcmp(config->lcp_renegotiation, "always"))
|
|
/* We always renegotiate */
|
|
return 0;
|
|
|
|
LOG(3, s, t, "Trying to reuse the LAC sent and received LCP configuration:\n");
|
|
if (config->debug >= 3)
|
|
{
|
|
dumplcp(sent_lcp, sent_lcp_n);
|
|
dumplcp(received_lcp, received_lcp_n);
|
|
}
|
|
|
|
/* Check what configuration the LAC sent */
|
|
if (!processsentlcpconfreq(s, t, NULL, sent_lcp, sent_lcp_n, &changed))
|
|
{
|
|
LOG(3, s, t, "Sent LCP conf does not match\n");
|
|
return 0;
|
|
}
|
|
|
|
/* Check what configuration the LAC received */
|
|
processreceivedlcpconfreq(s, t, NULL, received_lcp, received_lcp_n, &changed, &response);
|
|
if (response)
|
|
{
|
|
LOG(3, s, t, "Received LCP conf does not match\n");
|
|
/* Mismatch, renegotiate */
|
|
return 0;
|
|
}
|
|
LOG(3, s, t, "LCP conf match\n");
|
|
|
|
if (changed)
|
|
cluster_send_session(s);
|
|
|
|
// Ok!
|
|
return 1;
|
|
}
|
|
|
|
// Try to use auth proxy to avoid renegotiating it (in case the LAC forces PAP)
|
|
int processauthproxy(sessionidt s, tunnelidt t,
|
|
uint16_t authtype, const char *authname,
|
|
size_t authchalln, const char authchall[authchalln],
|
|
size_t authrespn, const char authresp[authrespn])
|
|
{
|
|
uint16_t r;
|
|
|
|
if ((sess_local[s].lcp_authtype == 0 && authtype != 0)
|
|
|| (sess_local[s].lcp_authtype == AUTHPAP && authtype != 3) // PAP
|
|
|| (sess_local[s].lcp_authtype == AUTHCHAP && authtype != 2)) // CHAP
|
|
{
|
|
LOG(3, s, t, "Authentication proxy mismatch: got %u (%s) while expecting %u\n",
|
|
authtype, ppp_auth_type(authtype), sess_local[s].lcp_authtype);
|
|
return 0;
|
|
}
|
|
|
|
if (session[s].ppp.phase != Authenticate)
|
|
{
|
|
LOG(3, s, t, "Already authenticated\n");
|
|
return 1;
|
|
}
|
|
|
|
if (sess_local[s].lcp_authtype == 0)
|
|
{
|
|
LOG(4, s, t, "No auth needed\n");
|
|
return 1;
|
|
}
|
|
|
|
if ((!config->disable_lac_func) && lac_conf_forwardtoremotelns(s, authname))
|
|
{
|
|
// Creating a tunnel/session has been started
|
|
return 1;
|
|
}
|
|
|
|
if (!(r = radiusnew(s)))
|
|
{
|
|
LOG(1, s, t, "No RADIUS session available to authenticate session...\n");
|
|
sessionshutdown(s, "No free RADIUS sessions.", CDN_UNAVAILABLE, TERM_SERVICE_UNAVAILABLE);
|
|
return 1;
|
|
}
|
|
|
|
// Run PRE_AUTH plugins
|
|
struct param_pre_auth packet = { &tunnel[t], &session[s], strdup(authname), NULL, 0, 1 };
|
|
|
|
if (sess_local[s].lcp_authtype == AUTHPAP)
|
|
{
|
|
packet.password = strndup(authresp, authrespn);
|
|
packet.protocol = PPPPAP;
|
|
}
|
|
else if (sess_local[s].lcp_authtype == AUTHCHAP)
|
|
{
|
|
if (authchalln != 16)
|
|
{
|
|
LOG(1, s, t, "CHAP challenge size different from 16: %zu\n", authchalln);
|
|
return 0;
|
|
}
|
|
if (authrespn != 16)
|
|
{
|
|
LOG(1, s, t, "CHAP response size different from 16: %zu\n", authrespn);
|
|
return 0;
|
|
}
|
|
packet.password = calloc(17, 1);
|
|
memcpy(packet.password, authresp, 16);
|
|
packet.protocol = PPPCHAP;
|
|
}
|
|
else
|
|
// Not PAP or CHAP??
|
|
return 0;
|
|
|
|
run_plugins(PLUGIN_PRE_AUTH, &packet);
|
|
if (!packet.continue_auth)
|
|
{
|
|
LOG(3, s, t, "A plugin rejected PRE_AUTH\n");
|
|
if (packet.username) free(packet.username);
|
|
if (packet.password) free(packet.password);
|
|
return 1;
|
|
}
|
|
|
|
// Take all authentication information from proxy, and record for possibly proxying ourself
|
|
strncpy(session[s].user, packet.username, sizeof(session[s].user) - 1);
|
|
strncpy((char *) sess_local[s].auth_name, packet.username, sizeof(sess_local[s].auth_name) - 1);
|
|
|
|
if (sess_local[s].lcp_authtype == AUTHPAP)
|
|
{
|
|
strncpy(radius[r].pass, packet.password, sizeof(radius[r].pass) - 1);
|
|
strncpy((char *) sess_local[s].auth_resp, packet.password, sizeof(sess_local[s].auth_resp) - 1);
|
|
}
|
|
else
|
|
{
|
|
memcpy(radius[r].auth, authchall, 16);
|
|
memcpy(sess_local[s].auth_chall, authchall, 16);
|
|
|
|
memcpy(radius[r].pass, packet.password, 16);
|
|
memcpy(sess_local[s].auth_resp, packet.password, 16);
|
|
|
|
radius[r].chap = 1;
|
|
}
|
|
radius[r].id = sess_local[s].auth_id;
|
|
|
|
LOG(3, s, t, "Sending login for %s to RADIUS\n", packet.username);
|
|
|
|
free(packet.username);
|
|
free(packet.password);
|
|
|
|
if ((session[s].mrru) && (!first_session_in_bundle(s)))
|
|
radiussend(r, RADIUSJUSTAUTH);
|
|
else
|
|
radiussend(r, RADIUSAUTH);
|
|
|
|
return 1;
|
|
}
|
|
|
|
// Process LCP messages
|
|
void processlcp(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l)
|
|
{
|
|
uint8_t b[MAXETHER];
|
|
uint8_t *q = NULL;
|
|
uint16_t hl;
|
|
|
|
CSTAT(processlcp);
|
|
|
|
LOG_HEX(5, "LCP", p, l);
|
|
if (l < 4)
|
|
{
|
|
LOG(1, s, t, "Short LCP %d bytes\n", l);
|
|
STAT(tunnel_rx_errors);
|
|
return ;
|
|
}
|
|
|
|
if ((hl = ntohs(*(uint16_t *) (p + 2))) > l)
|
|
{
|
|
LOG(1, s, t, "Length mismatch LCP %u/%u\n", hl, l);
|
|
STAT(tunnel_rx_errors);
|
|
return ;
|
|
}
|
|
l = hl;
|
|
|
|
if (session[s].die) // going down...
|
|
return;
|
|
|
|
LOG(((*p == EchoReq || *p == EchoReply) ? 4 : 3), s, t,
|
|
"LCP: recv %s\n", ppp_code(*p));
|
|
|
|
if (config->debug > 3) dumplcp(p, l);
|
|
|
|
if (*p == ConfigAck)
|
|
{
|
|
int x = l - 4;
|
|
uint8_t *o = (p + 4);
|
|
int authtype = 0;
|
|
|
|
while (x > 2)
|
|
{
|
|
int type = o[0];
|
|
int length = o[1];
|
|
|
|
if (length == 0 || type == 0 || x < length) break;
|
|
switch (type)
|
|
{
|
|
case 3: // Authentication-Protocol
|
|
{
|
|
int proto = ntohs(*(uint16_t *)(o + 2));
|
|
if (proto == PPPPAP)
|
|
authtype = AUTHPAP;
|
|
else if (proto == PPPCHAP && *(o + 4) == 5)
|
|
authtype = AUTHCHAP;
|
|
}
|
|
|
|
break;
|
|
}
|
|
x -= length;
|
|
o += length;
|
|
}
|
|
|
|
if (!session[s].ip && authtype)
|
|
sess_local[s].lcp_authtype = authtype;
|
|
|
|
switch (session[s].ppp.lcp)
|
|
{
|
|
case RequestSent:
|
|
initialise_restart_count(s, lcp);
|
|
change_state(s, lcp, AckReceived);
|
|
break;
|
|
|
|
case AckReceived:
|
|
case Opened:
|
|
LOG(2, s, t, "LCP: ConfigAck in state %s? Sending ConfigReq\n", ppp_state(session[s].ppp.lcp));
|
|
if (session[s].ppp.lcp == Opened)
|
|
lcp_restart(s);
|
|
|
|
sendlcp(s, t);
|
|
change_state(s, lcp, RequestSent);
|
|
break;
|
|
|
|
case AckSent:
|
|
lcp_open(s, t);
|
|
break;
|
|
|
|
default:
|
|
LOG(2, s, t, "LCP: ignoring %s in state %s\n", ppp_code(*p), ppp_state(session[s].ppp.lcp));
|
|
}
|
|
}
|
|
else if (*p == ConfigReq)
|
|
{
|
|
uint8_t *response = 0;
|
|
int changed = 0;
|
|
|
|
processreceivedlcpconfreq(s, t, p, p + 4, l - 4, &changed, &response);
|
|
|
|
if (changed)
|
|
cluster_send_session(s);
|
|
|
|
if (response)
|
|
{
|
|
l = q - response; // LCP packet length
|
|
*((uint16_t *) (response + 2)) = htons(l); // update header
|
|
}
|
|
else
|
|
{
|
|
// Send packet back as ConfigAck
|
|
response = makeppp(b, sizeof(b), p, l, s, t, PPPLCP, 0, 0, 0);
|
|
if (!response) return;
|
|
*response = ConfigAck;
|
|
}
|
|
|
|
switch (session[s].ppp.lcp)
|
|
{
|
|
case Closed:
|
|
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);
|
|
break;
|
|
|
|
case Stopped:
|
|
initialise_restart_count(s, lcp);
|
|
sendlcp(s, t);
|
|
if (*response == ConfigAck)
|
|
change_state(s, lcp, AckSent);
|
|
else
|
|
change_state(s, lcp, RequestSent);
|
|
|
|
break;
|
|
|
|
case RequestSent:
|
|
if (*response == ConfigAck)
|
|
change_state(s, lcp, AckSent);
|
|
|
|
break;
|
|
|
|
case AckReceived:
|
|
if (*response == ConfigAck)
|
|
lcp_open(s, t);
|
|
|
|
break;
|
|
|
|
case Opened:
|
|
lcp_restart(s);
|
|
sendlcp(s, t);
|
|
/* fallthrough */
|
|
|
|
case AckSent:
|
|
if (*response == ConfigAck)
|
|
change_state(s, lcp, AckSent);
|
|
else
|
|
change_state(s, lcp, RequestSent);
|
|
|
|
break;
|
|
|
|
case Closing:
|
|
sessionshutdown(s, "LCP: ConfigReq in state Closing. This should not happen. Killing session.", CDN_ADMIN_DISC, TERM_LOST_SERVICE);
|
|
break;
|
|
|
|
default:
|
|
LOG(2, s, t, "LCP: ignoring %s in state %s\n", ppp_code(*p), ppp_state(session[s].ppp.lcp));
|
|
return;
|
|
}
|
|
|
|
LOG(3, s, t, "LCP: send %s\n", ppp_code(*response));
|
|
if (config->debug > 3) dumplcp(response, l);
|
|
|
|
if (*response == ConfigAck && l - 4 <= sizeof(sess_local[s].lcp_last_received_confreq))
|
|
{
|
|
memcpy(sess_local[s].lcp_last_received_confreq, p + 4, l - 4);
|
|
sess_local[s].lcp_last_received_confreq_n = l - 4;
|
|
}
|
|
else
|
|
{
|
|
LOG(3, s, t, "their LCP ConfigReq is too long for proxying\n");
|
|
dumplcp(p, l);
|
|
sess_local[s].lcp_last_received_confreq_n = 0;
|
|
}
|
|
|
|
tunnelsend(b, l + (response - b), t);
|
|
}
|
|
else if (*p == ConfigNak || *p == ConfigRej)
|
|
{
|
|
int x = l - 4;
|
|
uint8_t *o = (p + 4);
|
|
int authtype = -1;
|
|
|
|
// They didn't like our config
|
|
|
|
sess_local[s].lcp_last_sent_confreq_n = 0;
|
|
|
|
while (x > 2)
|
|
{
|
|
int type = o[0];
|
|
int length = o[1];
|
|
|
|
if (length == 0 || type == 0 || x < length) break;
|
|
switch (type)
|
|
{
|
|
case 1: // Maximum-Receive-Unit
|
|
if (*p == ConfigNak)
|
|
{
|
|
if (length < 4) break;
|
|
sess_local[s].ppp_mru = ntohs(*(uint16_t *)(o + 2));
|
|
LOG(3, s, t, " Remote requested MRU of %u\n", sess_local[s].ppp_mru);
|
|
}
|
|
else
|
|
{
|
|
sess_local[s].ppp_mru = 0;
|
|
LOG(3, s, t, " Remote rejected MRU negotiation\n");
|
|
}
|
|
|
|
break;
|
|
|
|
case 3: // Authentication-Protocol
|
|
if (authtype > 0)
|
|
break;
|
|
|
|
if (*p == ConfigNak)
|
|
{
|
|
int proto;
|
|
|
|
if (length < 4) break;
|
|
proto = ntohs(*(uint16_t *)(o + 2));
|
|
|
|
if (proto == PPPPAP)
|
|
{
|
|
authtype = config->radius_authtypes & AUTHPAP;
|
|
LOG(3, s, t, " Remote requested PAP authentication...%sing\n",
|
|
authtype ? "accept" : "reject");
|
|
}
|
|
else if (proto == PPPCHAP && (length == 4 || (length > 4 && *(o + 4) == 5)))
|
|
{
|
|
authtype = config->radius_authtypes & AUTHCHAP;
|
|
LOG(3, s, t, " Remote requested CHAP authentication...%sing\n",
|
|
authtype ? "accept" : "reject");
|
|
}
|
|
else
|
|
{
|
|
LOG(3, s, t, " Rejecting unsupported authentication %#4x\n",
|
|
proto);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LOG(2, s, t, "LCP: remote rejected auth negotiation\n");
|
|
authtype = 0; // shutdown
|
|
}
|
|
|
|
break;
|
|
|
|
case 5: // Magic-Number
|
|
session[s].magic = 0;
|
|
if (*p == ConfigNak)
|
|
{
|
|
if (length < 6) break;
|
|
session[s].magic = ntohl(*(uint32_t *)(o + 2));
|
|
}
|
|
|
|
if (session[s].magic)
|
|
LOG(3, s, t, " Remote requested magic-no %x\n", session[s].magic);
|
|
else
|
|
LOG(3, s, t, " Remote rejected magic-no\n");
|
|
|
|
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);
|
|
return;
|
|
}
|
|
x -= length;
|
|
o += length;
|
|
}
|
|
|
|
if (!authtype)
|
|
{
|
|
sessionshutdown(s, "Unsupported authentication.", CDN_ADMIN_DISC, TERM_USER_ERROR);
|
|
return;
|
|
}
|
|
|
|
if (authtype > 0)
|
|
sess_local[s].lcp_authtype = authtype;
|
|
|
|
switch (session[s].ppp.lcp)
|
|
{
|
|
case Closed:
|
|
case Stopped:
|
|
{
|
|
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);
|
|
|
|
LOG(3, s, t, "LCP: send %s\n", ppp_code(*response));
|
|
if (config->debug > 3) dumplcp(response, l);
|
|
|
|
tunnelsend(b, l + (response - b), t);
|
|
}
|
|
break;
|
|
|
|
case RequestSent:
|
|
case AckSent:
|
|
initialise_restart_count(s, lcp);
|
|
sendlcp(s, t);
|
|
break;
|
|
|
|
case AckReceived:
|
|
LOG(2, s, t, "LCP: ConfigNak in state %s? Sending ConfigReq\n", ppp_state(session[s].ppp.lcp));
|
|
sendlcp(s, t);
|
|
break;
|
|
|
|
case Opened:
|
|
lcp_restart(s);
|
|
sendlcp(s, t);
|
|
break;
|
|
|
|
default:
|
|
LOG(2, s, t, "LCP: ignoring %s in state %s\n", ppp_code(*p), ppp_state(session[s].ppp.lcp));
|
|
return;
|
|
}
|
|
}
|
|
else if (*p == TerminateReq)
|
|
{
|
|
switch (session[s].ppp.lcp)
|
|
{
|
|
case Closed:
|
|
case Stopped:
|
|
case Closing:
|
|
case Stopping:
|
|
case RequestSent:
|
|
case AckReceived:
|
|
case AckSent:
|
|
break;
|
|
|
|
case Opened:
|
|
lcp_restart(s);
|
|
zero_restart_count(s, lcp);
|
|
change_state(s, lcp, Closing);
|
|
break;
|
|
|
|
default:
|
|
LOG(2, s, t, "LCP: ignoring %s in state %s\n", ppp_code(*p), ppp_state(session[s].ppp.lcp));
|
|
return;
|
|
}
|
|
|
|
*p = TerminateAck; // send ack
|
|
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));
|
|
if (config->debug > 3) dumplcp(q, l);
|
|
|
|
tunnelsend(b, l + (q - b), t); // send it
|
|
}
|
|
else if (*p == ProtocolRej)
|
|
{
|
|
uint16_t proto = 0;
|
|
|
|
if (l > 4)
|
|
{
|
|
proto = *(p+4);
|
|
if (l > 5 && !(proto & 1))
|
|
{
|
|
proto <<= 8;
|
|
proto |= *(p+5);
|
|
}
|
|
}
|
|
|
|
if (proto == PPPIPV6CP)
|
|
{
|
|
LOG(3, s, t, "IPv6 rejected\n");
|
|
change_state(s, ipv6cp, Closed);
|
|
}
|
|
else
|
|
{
|
|
LOG(3, s, t, "LCP protocol reject: 0x%04X\n", proto);
|
|
}
|
|
}
|
|
else if (*p == EchoReq)
|
|
{
|
|
*p = EchoReply; // reply
|
|
*(uint32_t *) (p + 4) = htonl(session[s].magic); // our magic number
|
|
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));
|
|
if (config->debug > 3) dumplcp(q, l);
|
|
|
|
tunnelsend(b, l + (q - b), t); // send it
|
|
}
|
|
else if (*p == EchoReply)
|
|
{
|
|
// Ignore it, last_packet time is set earlier than this.
|
|
}
|
|
else if (*p != CodeRej)
|
|
{
|
|
ppp_code_rej(s, t, PPPLCP, "LCP", p, l, b, sizeof(b));
|
|
}
|
|
}
|
|
|
|
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))
|
|
{
|
|
sessionidt first_ses = bundle[i].members[0];
|
|
if (bundle[i].mssf != session[s].mssf)
|
|
{
|
|
// uniformity of sequence number format must be insured
|
|
LOG(3, s, session[s].tunnel, "MPPP: unable to bundle session %d in bundle %d cause of different mssf\n", s, i);
|
|
return -1;
|
|
}
|
|
session[s].bundle = i;
|
|
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;
|
|
|
|
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;
|
|
// FIXME !!! to enable l2tpns reading mssf frames receiver_max_seq, sender_max_seq must be introduce
|
|
// now session[s].mssf flag indecates that the receiver wish to receive frames in mssf, so max_seq (i.e. recv_max_seq) = 1<<24
|
|
/*
|
|
if (bundle[b].mssf)
|
|
bundle[b].max_seq = 1 << 12;
|
|
else */
|
|
bundle[b].max_seq = 1 << 24;
|
|
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;
|
|
bundle[b].timeout = session[s].timeout;
|
|
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
|
|
memset(&frag[i], 0, sizeof(fragmentationt));
|
|
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");
|
|
|
|
change_state(s, ipcp, Opened);
|
|
|
|
if (!(session[s].walled_garden || session[s].flags & SESSION_STARTED))
|
|
{
|
|
uint16_t r = radiusnew(s);
|
|
if (r)
|
|
{
|
|
radiussend(r, RADIUSSTART); // send radius start
|
|
|
|
// don't send further Start records if IPCP is restarted
|
|
session[s].flags |= SESSION_STARTED;
|
|
cluster_send_session(s);
|
|
}
|
|
}
|
|
|
|
// start IPv6 if configured and still in passive state
|
|
if (session[s].ppp.ipv6cp == Stopped)
|
|
{
|
|
sendipv6cp(s, t);
|
|
change_state(s, ipv6cp, RequestSent);
|
|
}
|
|
}
|
|
|
|
// Process IPCP messages
|
|
void processipcp(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l)
|
|
{
|
|
uint8_t b[MAXETHER];
|
|
uint8_t *q = 0;
|
|
uint16_t hl;
|
|
|
|
CSTAT(processipcp);
|
|
|
|
LOG_HEX(5, "IPCP", p, l);
|
|
if (l < 4)
|
|
{
|
|
LOG(1, s, t, "Short IPCP %d bytes\n", l);
|
|
STAT(tunnel_rx_errors);
|
|
return ;
|
|
}
|
|
|
|
if ((hl = ntohs(*(uint16_t *) (p + 2))) > l)
|
|
{
|
|
LOG(1, s, t, "Length mismatch IPCP %u/%u\n", hl, l);
|
|
STAT(tunnel_rx_errors);
|
|
return ;
|
|
}
|
|
l = hl;
|
|
|
|
if (session[s].ppp.phase < Network)
|
|
{
|
|
LOG(2, s, t, "IPCP %s ignored in %s phase\n", ppp_code(*p), ppp_phase(session[s].ppp.phase));
|
|
return;
|
|
}
|
|
|
|
LOG(3, s, t, "IPCP: recv %s\n", ppp_code(*p));
|
|
|
|
if (*p == ConfigAck)
|
|
{
|
|
switch (session[s].ppp.ipcp)
|
|
{
|
|
case RequestSent:
|
|
initialise_restart_count(s, ipcp);
|
|
change_state(s, ipcp, AckReceived);
|
|
break;
|
|
|
|
case AckReceived:
|
|
case Opened:
|
|
LOG(2, s, t, "IPCP: ConfigAck in state %s? Sending ConfigReq\n", ppp_state(session[s].ppp.ipcp));
|
|
sendipcp(s, t);
|
|
change_state(s, ipcp, RequestSent);
|
|
break;
|
|
|
|
case AckSent:
|
|
ipcp_open(s, t);
|
|
break;
|
|
|
|
default:
|
|
LOG(2, s, t, "IPCP: ignoring %s in state %s\n", ppp_code(*p), ppp_state(session[s].ppp.ipcp));
|
|
}
|
|
}
|
|
else if (*p == ConfigReq)
|
|
{
|
|
uint8_t *response = 0;
|
|
uint8_t *o = p + 4;
|
|
int length = l - 4;
|
|
int gotip = 0;
|
|
in_addr_t addr;
|
|
|
|
while (length > 2)
|
|
{
|
|
if (!o[1] || o[1] == 1 || o[1] > length) return;
|
|
|
|
switch (*o)
|
|
{
|
|
case 3: // ip address
|
|
gotip++; // seen address
|
|
if (o[1] != 6) return;
|
|
|
|
addr = htonl(session[s].ip);
|
|
if (memcmp(o + 2, &addr, (sizeof addr)))
|
|
{
|
|
uint8_t *oq = q;
|
|
q = ppp_conf_nak(s, b, sizeof(b), PPPIPCP, &response, q, p, o, (uint8_t *) &addr, sizeof(addr));
|
|
if (!q || (q != oq && *response == ConfigRej))
|
|
{
|
|
sessionshutdown(s, "Can't negotiate IPCP.", CDN_ADMIN_DISC, TERM_USER_ERROR);
|
|
return;
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case 129: // primary DNS
|
|
if (o[1] != 6) return;
|
|
|
|
addr = htonl(session[s].dns1);
|
|
if (memcmp(o + 2, &addr, (sizeof addr)))
|
|
{
|
|
q = ppp_conf_nak(s, b, sizeof(b), PPPIPCP, &response, q, p, o, (uint8_t *) &addr, sizeof(addr));
|
|
if (!q) return;
|
|
}
|
|
|
|
break;
|
|
|
|
case 131: // secondary DNS
|
|
if (o[1] != 6) return;
|
|
|
|
addr = htonl(session[s].dns2);
|
|
if (memcmp(o + 2, &addr, sizeof(addr)))
|
|
{
|
|
q = ppp_conf_nak(s, b, sizeof(b), PPPIPCP, &response, q, p, o, (uint8_t *) &addr, sizeof(addr));
|
|
if (!q) return;
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
LOG(2, s, t, " Rejecting PPP IPCP Option type %d\n", *o);
|
|
q = ppp_conf_rej(s, b, sizeof(b), PPPIPCP, &response, q, p, o);
|
|
if (!q) return;
|
|
}
|
|
|
|
length -= o[1];
|
|
o += o[1];
|
|
}
|
|
|
|
if (response)
|
|
{
|
|
l = q - response; // IPCP packet length
|
|
*((uint16_t *) (response + 2)) = htons(l); // update header
|
|
}
|
|
else if (gotip)
|
|
{
|
|
// Send packet back as ConfigAck
|
|
response = makeppp(b, sizeof(b), p, l, s, t, PPPIPCP, 0, 0, 0);
|
|
if (!response) return;
|
|
*response = ConfigAck;
|
|
}
|
|
else
|
|
{
|
|
LOG(1, s, t, "No IP in IPCP request\n");
|
|
STAT(tunnel_rx_errors);
|
|
return;
|
|
}
|
|
|
|
switch (session[s].ppp.ipcp)
|
|
{
|
|
case Closed:
|
|
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);
|
|
break;
|
|
|
|
case Stopped:
|
|
initialise_restart_count(s, ipcp);
|
|
sendipcp(s, t);
|
|
if (*response == ConfigAck)
|
|
change_state(s, ipcp, AckSent);
|
|
else
|
|
change_state(s, ipcp, RequestSent);
|
|
|
|
break;
|
|
|
|
case RequestSent:
|
|
if (*response == ConfigAck)
|
|
change_state(s, ipcp, AckSent);
|
|
|
|
break;
|
|
|
|
case AckReceived:
|
|
if (*response == ConfigAck)
|
|
ipcp_open(s, t);
|
|
|
|
break;
|
|
|
|
case Opened:
|
|
initialise_restart_count(s, ipcp);
|
|
sendipcp(s, t);
|
|
/* fallthrough */
|
|
|
|
case AckSent:
|
|
if (*response == ConfigAck)
|
|
change_state(s, ipcp, AckSent);
|
|
else
|
|
change_state(s, ipcp, RequestSent);
|
|
|
|
break;
|
|
|
|
default:
|
|
LOG(2, s, t, "IPCP: ignoring %s in state %s\n", ppp_code(*p), ppp_state(session[s].ppp.ipcp));
|
|
return;
|
|
}
|
|
|
|
LOG(3, s, t, "IPCP: send %s\n", ppp_code(*response));
|
|
tunnelsend(b, l + (response - b), t);
|
|
}
|
|
else if (*p == TerminateReq)
|
|
{
|
|
switch (session[s].ppp.ipcp)
|
|
{
|
|
case Closed:
|
|
case Stopped:
|
|
case Closing:
|
|
case Stopping:
|
|
case RequestSent:
|
|
case AckReceived:
|
|
case AckSent:
|
|
break;
|
|
|
|
case Opened:
|
|
zero_restart_count(s, ipcp);
|
|
change_state(s, ipcp, Closing);
|
|
break;
|
|
|
|
default:
|
|
LOG(2, s, t, "IPCP: ignoring %s in state %s\n", ppp_code(*p), ppp_state(session[s].ppp.ipcp));
|
|
return;
|
|
}
|
|
|
|
*p = TerminateAck; // send ack
|
|
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));
|
|
tunnelsend(b, l + (q - b), t); // send it
|
|
}
|
|
else if (*p != CodeRej)
|
|
{
|
|
ppp_code_rej(s, t, PPPIPCP, "IPCP", p, l, b, sizeof(b));
|
|
}
|
|
}
|
|
|
|
static void ipv6cp_open(sessionidt s, tunnelidt t)
|
|
{
|
|
LOG(3, s, t, "IPV6CP: Opened\n");
|
|
|
|
change_state(s, ipv6cp, Opened);
|
|
routes6set(s, &session[s], 1);
|
|
|
|
// Send an initial RA
|
|
send_ipv6_ra(s, t, NULL);
|
|
sess_local[s].last_ra = time_now;
|
|
}
|
|
|
|
// Process IPV6CP messages
|
|
void processipv6cp(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l)
|
|
{
|
|
uint8_t b[MAXETHER];
|
|
uint8_t *q = 0;
|
|
uint16_t hl;
|
|
|
|
CSTAT(processipv6cp);
|
|
|
|
LOG_HEX(5, "IPV6CP", p, l);
|
|
if (l < 4)
|
|
{
|
|
LOG(1, s, t, "Short IPV6CP %d bytes\n", l);
|
|
STAT(tunnel_rx_errors);
|
|
return ;
|
|
}
|
|
|
|
if ((hl = ntohs(*(uint16_t *) (p + 2))) > l)
|
|
{
|
|
LOG(1, s, t, "Length mismatch IPV6CP %u/%u\n", hl, l);
|
|
STAT(tunnel_rx_errors);
|
|
return ;
|
|
}
|
|
l = hl;
|
|
|
|
if (session[s].ppp.phase < Network)
|
|
{
|
|
LOG(2, s, t, "IPV6CP %s ignored in %s phase\n", ppp_code(*p), ppp_phase(session[s].ppp.phase));
|
|
return;
|
|
}
|
|
|
|
LOG(3, s, t, "IPV6CP: recv %s\n", ppp_code(*p));
|
|
|
|
if (!session[s].ip)
|
|
{
|
|
LOG(3, s, t, "IPV6CP: no IPv4 address (IPCP in state %s)\n", ppp_state(session[s].ppp.ipcp));
|
|
return; // need IPCP to complete...
|
|
}
|
|
|
|
if (*p == ConfigAck)
|
|
{
|
|
switch (session[s].ppp.ipv6cp)
|
|
{
|
|
case RequestSent:
|
|
initialise_restart_count(s, ipv6cp);
|
|
change_state(s, ipv6cp, AckReceived);
|
|
break;
|
|
|
|
case AckReceived:
|
|
case Opened:
|
|
LOG(2, s, t, "IPV6CP: ConfigAck in state %s? Sending ConfigReq\n", ppp_state(session[s].ppp.ipv6cp));
|
|
sendipv6cp(s, t);
|
|
change_state(s, ipv6cp, RequestSent);
|
|
break;
|
|
|
|
case AckSent:
|
|
ipv6cp_open(s, t);
|
|
break;
|
|
|
|
default:
|
|
LOG(2, s, t, "IPV6CP: ignoring %s in state %s\n", ppp_code(*p), ppp_state(session[s].ppp.ipv6cp));
|
|
}
|
|
}
|
|
else if (*p == ConfigReq)
|
|
{
|
|
uint8_t *response = 0;
|
|
uint8_t *o = p + 4;
|
|
int length = l - 4;
|
|
int gotip = 0;
|
|
uint32_t ident[2];
|
|
|
|
if (session[s].ipv6address.s6_addr[0])
|
|
{
|
|
// LSB 64bits of assigned IPv6 address to user (see radius attribut Framed-IPv6-Address)
|
|
memcpy(&ident[0], &session[s].ipv6address.s6_addr[8], 8);
|
|
}
|
|
else
|
|
{
|
|
ident[0] = htonl(session[s].ip);
|
|
ident[1] = 0;
|
|
}
|
|
|
|
while (length > 2)
|
|
{
|
|
if (!o[1] || o[1] == 1 || o[1] > length) return;
|
|
|
|
switch (*o)
|
|
{
|
|
case 1: // interface identifier
|
|
gotip++; // seen address
|
|
if (o[1] != 10) return;
|
|
|
|
if (memcmp(o + 2, ident, sizeof(ident)))
|
|
{
|
|
q = ppp_conf_nak(s, b, sizeof(b), PPPIPV6CP, &response, q, p, o, (uint8_t *)ident, sizeof(ident));
|
|
if (!q) return;
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
LOG(2, s, t, " Rejecting PPP IPV6CP Option type %d\n", *o);
|
|
q = ppp_conf_rej(s, b, sizeof(b), PPPIPV6CP, &response, q, p, o);
|
|
if (!q) return;
|
|
}
|
|
|
|
length -= o[1];
|
|
o += o[1];
|
|
}
|
|
|
|
if (!response && !gotip && sess_local[s].tried_identifier++ < 2)
|
|
{
|
|
uint8_t identifier_option[6] = { 1, 6 };
|
|
|
|
// No interface identifier option, try to suggest one
|
|
q = ppp_conf_nak(s, b, sizeof(b), PPPIPV6CP, &response, q, p, identifier_option, (uint8_t *)ident, sizeof(ident));
|
|
if (!q) return;
|
|
}
|
|
|
|
if (response)
|
|
{
|
|
l = q - response; // IPV6CP packet length
|
|
*((uint16_t *) (response + 2)) = htons(l); // update header
|
|
}
|
|
else
|
|
{
|
|
if (!gotip)
|
|
LOG(2, s, t, "No interface identifier in IPV6CP request, hoping for the best\n");
|
|
|
|
// Send packet back as ConfigAck
|
|
response = makeppp(b, sizeof(b), p, l, s, t, PPPIPV6CP, 0, 0, 0);
|
|
if (!response) return;
|
|
*response = ConfigAck;
|
|
}
|
|
|
|
switch (session[s].ppp.ipv6cp)
|
|
{
|
|
case Closed:
|
|
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);
|
|
break;
|
|
|
|
case Stopped:
|
|
initialise_restart_count(s, ipv6cp);
|
|
sendipv6cp(s, t);
|
|
if (*response == ConfigAck)
|
|
change_state(s, ipv6cp, AckSent);
|
|
else
|
|
change_state(s, ipv6cp, RequestSent);
|
|
|
|
break;
|
|
|
|
case RequestSent:
|
|
if (*response == ConfigAck)
|
|
change_state(s, ipv6cp, AckSent);
|
|
|
|
break;
|
|
|
|
case AckReceived:
|
|
if (*response == ConfigAck)
|
|
ipv6cp_open(s, t);
|
|
|
|
break;
|
|
|
|
case Opened:
|
|
initialise_restart_count(s, ipv6cp);
|
|
sendipv6cp(s, t);
|
|
/* fallthrough */
|
|
|
|
case AckSent:
|
|
if (*response == ConfigAck)
|
|
change_state(s, ipv6cp, AckSent);
|
|
else
|
|
change_state(s, ipv6cp, RequestSent);
|
|
|
|
break;
|
|
|
|
default:
|
|
LOG(2, s, t, "IPV6CP: ignoring %s in state %s\n", ppp_code(*p), ppp_state(session[s].ppp.ipv6cp));
|
|
return;
|
|
}
|
|
|
|
LOG(3, s, t, "IPV6CP: send %s\n", ppp_code(*response));
|
|
tunnelsend(b, l + (response - b), t);
|
|
}
|
|
else if (*p == TerminateReq)
|
|
{
|
|
switch (session[s].ppp.ipv6cp)
|
|
{
|
|
case Closed:
|
|
case Stopped:
|
|
case Closing:
|
|
case Stopping:
|
|
case RequestSent:
|
|
case AckReceived:
|
|
case AckSent:
|
|
break;
|
|
|
|
case Opened:
|
|
zero_restart_count(s, ipv6cp);
|
|
change_state(s, ipv6cp, Closing);
|
|
break;
|
|
|
|
default:
|
|
LOG(2, s, t, "IPV6CP: ignoring %s in state %s\n", ppp_code(*p), ppp_state(session[s].ppp.ipv6cp));
|
|
return;
|
|
}
|
|
|
|
*p = TerminateAck; // send ack
|
|
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));
|
|
tunnelsend(b, l + (q - b), t); // send it
|
|
}
|
|
else if (*p != CodeRej)
|
|
{
|
|
ppp_code_rej(s, t, PPPIPV6CP, "IPV6CP", p, l, b, sizeof(b));
|
|
}
|
|
}
|
|
|
|
static void update_sessions_in_stat(sessionidt s, uint16_t l)
|
|
{
|
|
bundleidt b = session[s].bundle;
|
|
if (!b)
|
|
{
|
|
update_session_in_stat(s, 1, l);
|
|
}
|
|
else
|
|
{
|
|
int i = frag[b].re_frame_begin_index;
|
|
int end = frag[b].re_frame_end_index;
|
|
for (;;)
|
|
{
|
|
l = frag[b].fragment[i].length;
|
|
s = frag[b].fragment[i].sid;
|
|
update_session_in_stat(s, 1, l);
|
|
|
|
if (i == end)
|
|
return;
|
|
i = (i + 1) & MAXFRAGNUM_MASK;
|
|
}
|
|
}
|
|
}
|
|
|
|
// process IP packet received
|
|
//
|
|
// This MUST be called with at least 4 byte behind 'p'.
|
|
// (i.e. this routine writes to p[-4]).
|
|
void processipin(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l)
|
|
{
|
|
in_addr_t ip, ip_dst;
|
|
|
|
CSTAT(processipin);
|
|
|
|
LOG_HEX(5, "IP", p, l);
|
|
|
|
if (l < 20)
|
|
{
|
|
LOG(1, s, t, "IP packet too short %d\n", l);
|
|
STAT(tunnel_rx_errors);
|
|
return ;
|
|
}
|
|
|
|
ip = ntohl(*(uint32_t *)(p + 12));
|
|
ip_dst = *(uint32_t *)(p + 16);
|
|
|
|
if (l > MAXETHER)
|
|
{
|
|
LOG(1, s, t, "IP packet too long %d\n", l);
|
|
STAT(tunnel_rx_errors);
|
|
return ;
|
|
}
|
|
|
|
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 (!config->disable_no_spoof && ip != session[s].ip && sessionbyip(htonl(ip)) != s)
|
|
{
|
|
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))
|
|
return;
|
|
|
|
// adjust MSS on SYN and SYN,ACK packets with options
|
|
if ((ntohs(*(uint16_t *) (p + 6)) & 0x1fff) == 0 && p[9] == IPPROTO_TCP) // first tcp fragment
|
|
{
|
|
int ihl = (p[0] & 0xf) * 4; // length of IP header
|
|
if (l >= ihl + 20 && (p[ihl + 13] & TCP_FLAG_SYN) && ((p[ihl + 12] >> 4) > 5))
|
|
adjust_tcp4_mss(s, t, p, l, p + ihl);
|
|
}
|
|
|
|
// Add on the tun header
|
|
p -= 4;
|
|
*(uint32_t *) p = htonl(PKTIP);
|
|
l += 4;
|
|
|
|
if (session[s].tbf_in)
|
|
{
|
|
if (!config->no_throttle_local_IP || !sessionbyip(ip_dst))
|
|
{
|
|
// Are we throttling this session?
|
|
if (config->cluster_iam_master)
|
|
tbf_queue_packet(session[s].tbf_in, p, l);
|
|
else
|
|
master_throttle_packet(session[s].tbf_in, p, l);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// send to ethernet
|
|
if (tun_write(p, l) < 0)
|
|
{
|
|
STAT(tun_tx_errors);
|
|
LOG(0, s, t, "Error writing %d bytes to TUN device: %s (tunfd=%d, p=%p)\n",
|
|
l, strerror(errno), tunfd, p);
|
|
|
|
return;
|
|
}
|
|
|
|
p += 4;
|
|
l -= 4;
|
|
|
|
if (session[s].snoop_ip && session[s].snoop_port)
|
|
{
|
|
// Snooping this session
|
|
snoop_send_packet(p, l, session[s].snoop_ip, session[s].snoop_port);
|
|
}
|
|
|
|
update_sessions_in_stat(s, l);
|
|
|
|
eth_tx += l;
|
|
|
|
STAT(tun_tx_packets);
|
|
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;
|
|
bundlet * this_bundle = &bundle[b];
|
|
uint32_t maskSeq, max_seq;
|
|
int frag_offset;
|
|
uint16_t frag_index, frag_index_next, frag_index_prev;
|
|
fragmentationt *this_fragmentation = &frag[b];
|
|
uint8_t begin_frame = (*p & MP_BEGIN);
|
|
uint8_t end_frame = (*p & MP_END);
|
|
uint32_t seq_num, seq_num_next, seq_num_prev;
|
|
uint32_t i;
|
|
uint8_t flags = *p;
|
|
uint16_t begin_index, end_index;
|
|
|
|
// Perform length checking
|
|
if(l > MAXFRAGLEN)
|
|
{
|
|
LOG(2, s, t, "MPPP: discarding fragment larger than MAXFRAGLEN\n");
|
|
return;
|
|
}
|
|
|
|
if(!b)
|
|
{
|
|
LOG(2, s, t, "MPPP: Invalid bundle id: 0\n");
|
|
return;
|
|
}
|
|
// FIXME !! session[s].mssf means that the receiver wants to receive frames in mssf not means the receiver will send frames in mssf
|
|
/* if(session[s].mssf)
|
|
{
|
|
// Get 12 bit for seq number
|
|
seq_num = ntohs((*(uint16_t *) p) & 0xFF0F);
|
|
p += 2;
|
|
l -= 2;
|
|
// After this point the pointer should be advanced 2 bytes
|
|
LOG(3, s, t, "MPPP: 12 bits, sequence number: %d\n",seq_num);
|
|
}
|
|
else */
|
|
{
|
|
// Get 24 bit for seq number
|
|
seq_num = ntohl((*(uint32_t *) p) & 0xFFFFFF00);
|
|
p += 4;
|
|
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);
|
|
}
|
|
|
|
max_seq = this_bundle->max_seq;
|
|
maskSeq = max_seq - 1;
|
|
|
|
/*
|
|
* Expand sequence number to 32 bits, making it as close
|
|
* as possible to this_fragmentation->M.
|
|
*/
|
|
seq_num |= this_fragmentation->M & ~maskSeq;
|
|
if ((int)(this_fragmentation->M - seq_num) > (int)(maskSeq >> 1))
|
|
{
|
|
seq_num += maskSeq + 1;
|
|
}
|
|
else if ((int)(seq_num - this_fragmentation->M) > (int)(maskSeq >> 1))
|
|
{
|
|
seq_num -= maskSeq + 1; /* should never happen */
|
|
}
|
|
|
|
// calculate this fragment's offset from the begin seq in the bundle
|
|
frag_offset = (int) (seq_num - this_fragmentation->start_seq);
|
|
|
|
sess_local[s].last_seq = seq_num;
|
|
|
|
// calculate the jitter average
|
|
uint32_t ljitter = time_now_ms - sess_local[s].prev_time;
|
|
if (ljitter > 0)
|
|
{
|
|
sess_local[s].jitteravg = (sess_local[s].jitteravg + ljitter)>>1;
|
|
sess_local[s].prev_time = time_now_ms;
|
|
}
|
|
|
|
uint32_t Mmin;
|
|
|
|
if (seq_num < this_fragmentation->M)
|
|
{
|
|
Mmin = seq_num;
|
|
this_fragmentation->M = seq_num;
|
|
}
|
|
else
|
|
{
|
|
Mmin = sess_local[(this_bundle->members[0])].last_seq;
|
|
for (i = 1; i < this_bundle->num_of_links; i++)
|
|
{
|
|
uint32_t s_seq = sess_local[(this_bundle->members[i])].last_seq;
|
|
if (s_seq < Mmin)
|
|
Mmin = s_seq;
|
|
}
|
|
this_fragmentation->M = Mmin;
|
|
}
|
|
|
|
// calculate M offset of the M seq in the bundle
|
|
int M_offset = (int) (Mmin - this_fragmentation->start_seq);
|
|
|
|
if (M_offset >= MAXFRAGNUM)
|
|
{
|
|
// There have a long break of the link !!!!!!!!
|
|
// M_offset is bigger that the fragmentation buffer size
|
|
LOG(3, s, t, "MPPP: M_offset out of range, min:%d, begin_seq:%d\n", Mmin, this_fragmentation->start_seq);
|
|
|
|
// Calculate the new start index, the previous frag are lost
|
|
begin_index = (M_offset + this_fragmentation->start_index) & MAXFRAGNUM_MASK;
|
|
|
|
// Set new Start sequence
|
|
this_fragmentation->start_index = begin_index;
|
|
this_fragmentation->start_seq = Mmin;
|
|
M_offset = 0;
|
|
// recalculate the fragment offset from the new begin seq in the bundle
|
|
frag_offset = (int) (seq_num - Mmin);
|
|
}
|
|
|
|
// discard this fragment if the packet comes before the start sequence
|
|
if (frag_offset < 0)
|
|
{
|
|
// this packet comes before the next
|
|
LOG(3, s, t, "MPPP: (COMES BEFORE) the next, seq:%d, begin_seq:%d, size_frag:%d, flags:%02X is LOST\n", seq_num, this_fragmentation->start_seq, l, flags);
|
|
return;
|
|
}
|
|
|
|
// discard if frag_offset is bigger that the fragmentation buffer size
|
|
if (frag_offset >= MAXFRAGNUM)
|
|
{
|
|
// frag_offset is bigger that the fragmentation buffer size
|
|
LOG(3, s, t, "MPPP: Index out of range, seq:%d, begin_seq:%d\n", seq_num, this_fragmentation->start_seq);
|
|
return;
|
|
}
|
|
|
|
//caculate received fragment's index in the fragment array
|
|
frag_index = (frag_offset + this_fragmentation->start_index) & MAXFRAGNUM_MASK;
|
|
|
|
// insert the frame in it's place
|
|
fragmentt *this_frag = &this_fragmentation->fragment[frag_index];
|
|
|
|
if (this_frag->length > 0)
|
|
// This fragment is lost, It was around the buffer and it was never completed the packet.
|
|
LOG(3, this_frag->sid, this_frag->tid, "MPPP: (INSERT) seq_num:%d frag_index:%d flags:%02X is LOST\n",
|
|
this_frag->seq, frag_index, this_frag->flags);
|
|
|
|
this_frag->length = l;
|
|
this_frag->sid = s;
|
|
this_frag->tid = t;
|
|
this_frag->flags = flags;
|
|
this_frag->seq = seq_num;
|
|
this_frag->jitteravg = sess_local[s].jitteravg;
|
|
memcpy(this_frag->data, p, l);
|
|
|
|
LOG(4, s, t, "MPPP: seq_num:%d frag_index:%d INSERTED flags: %02X\n", seq_num, frag_index, flags);
|
|
|
|
//next frag index
|
|
frag_index_next = (frag_index + 1) & MAXFRAGNUM_MASK;
|
|
//previous frag index
|
|
frag_index_prev = (frag_index - 1) & MAXFRAGNUM_MASK;
|
|
// next seq
|
|
seq_num_next = seq_num + 1;
|
|
// previous seq
|
|
seq_num_prev = seq_num - 1;
|
|
|
|
// Clean the buffer and log the lost fragments
|
|
if ((frag_index_next != this_fragmentation->start_index) && this_fragmentation->fragment[frag_index_next].length)
|
|
{
|
|
// check if the next frag is a lost fragment
|
|
if (this_fragmentation->fragment[frag_index_next].seq != seq_num_next)
|
|
{
|
|
// This fragment is lost, It was around the buffer and it was never completed the packet.
|
|
LOG(3, this_fragmentation->fragment[frag_index_next].sid, this_fragmentation->fragment[frag_index_next].tid,
|
|
"MPPP: (NEXT) seq_num:%d frag_index:%d flags:%02X is LOST\n",
|
|
this_fragmentation->fragment[frag_index_next].seq, frag_index_next,
|
|
this_fragmentation->fragment[frag_index_next].flags);
|
|
// this frag is lost
|
|
this_fragmentation->fragment[frag_index_next].length = 0;
|
|
this_fragmentation->fragment[frag_index_next].flags = 0;
|
|
|
|
if (begin_frame && (!end_frame)) return; // assembling frame failed
|
|
}
|
|
}
|
|
|
|
// Clean the buffer and log the lost fragments
|
|
if ((frag_index != this_fragmentation->start_index) && this_fragmentation->fragment[frag_index_prev].length)
|
|
{
|
|
// check if the next frag is a lost fragment
|
|
if (this_fragmentation->fragment[frag_index_prev].seq != seq_num_prev)
|
|
{
|
|
// This fragment is lost, It was around the buffer and it was never completed the packet.
|
|
LOG(3, this_fragmentation->fragment[frag_index_prev].sid, this_fragmentation->fragment[frag_index_prev].tid,
|
|
"MPPP: (PREV) seq_num:%d frag_index:%d flags:%02X is LOST\n",
|
|
this_fragmentation->fragment[frag_index_prev].seq, frag_index_prev,
|
|
this_fragmentation->fragment[frag_index_prev].flags);
|
|
|
|
this_fragmentation->fragment[frag_index_prev].length = 0;
|
|
this_fragmentation->fragment[frag_index_prev].flags = 0;
|
|
|
|
if (end_frame && (!begin_frame)) return; // assembling frame failed
|
|
}
|
|
}
|
|
|
|
find_frame:
|
|
begin_index = this_fragmentation->start_index;
|
|
uint32_t b_seq = this_fragmentation->start_seq;
|
|
// Try to find a Begin sequence from the start_seq sequence to M sequence
|
|
while (b_seq < Mmin)
|
|
{
|
|
if (this_fragmentation->fragment[begin_index].length)
|
|
{
|
|
if (b_seq == this_fragmentation->fragment[begin_index].seq)
|
|
{
|
|
if (this_fragmentation->fragment[begin_index].flags & MP_BEGIN)
|
|
{
|
|
int isfoundE = 0;
|
|
// Adjust the new start sequence and start index
|
|
this_fragmentation->start_index = begin_index;
|
|
this_fragmentation->start_seq = b_seq;
|
|
// Begin Sequence found, now try to found the End Sequence to complete the frame
|
|
end_index = begin_index;
|
|
while (b_seq < Mmin)
|
|
{
|
|
if (this_fragmentation->fragment[end_index].length)
|
|
{
|
|
if (b_seq == this_fragmentation->fragment[end_index].seq)
|
|
{
|
|
if (this_fragmentation->fragment[end_index].flags & MP_END)
|
|
{
|
|
// The End sequence was found and the frame is complete
|
|
isfoundE = 1;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// This fragment is lost, it was never completed the packet.
|
|
LOG(3, this_fragmentation->fragment[end_index].sid, this_fragmentation->fragment[end_index].tid,
|
|
"MPPP: (FIND END) seq_num:%d frag_index:%d flags:%02X is LOST\n",
|
|
this_fragmentation->fragment[end_index].seq, begin_index,
|
|
this_fragmentation->fragment[end_index].flags);
|
|
// this frag is lost
|
|
this_fragmentation->fragment[end_index].length = 0;
|
|
this_fragmentation->fragment[end_index].flags = 0;
|
|
// This frame is not complete find the next Begin
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// This frame is not complete find the next Begin if exist
|
|
break;
|
|
}
|
|
end_index = (end_index +1) & MAXFRAGNUM_MASK;
|
|
b_seq++;
|
|
}
|
|
|
|
if (isfoundE)
|
|
// The End sequence was found and the frame is complete
|
|
break;
|
|
else
|
|
// find the next Begin
|
|
begin_index = end_index;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// This fragment is lost, it was never completed the packet.
|
|
LOG(3, this_fragmentation->fragment[begin_index].sid, this_fragmentation->fragment[begin_index].tid,
|
|
"MPPP: (FIND BEGIN) seq_num:%d frag_index:%d flags:%02X is LOST\n",
|
|
this_fragmentation->fragment[begin_index].seq, begin_index,
|
|
this_fragmentation->fragment[begin_index].flags);
|
|
// this frag is lost
|
|
this_fragmentation->fragment[begin_index].length = 0;
|
|
this_fragmentation->fragment[begin_index].flags = 0;
|
|
}
|
|
}
|
|
begin_index = (begin_index +1) & MAXFRAGNUM_MASK;
|
|
b_seq++;
|
|
}
|
|
|
|
assembling_frame:
|
|
// try to assemble the frame that has the received fragment as a member
|
|
// get the beginning of this frame
|
|
begin_index = end_index = this_fragmentation->start_index;
|
|
if (this_fragmentation->fragment[begin_index].length)
|
|
{
|
|
if (!(this_fragmentation->fragment[begin_index].flags & MP_BEGIN))
|
|
{
|
|
LOG(3, this_fragmentation->fragment[begin_index].sid, this_fragmentation->fragment[begin_index].tid,
|
|
"MPPP: (NOT BEGIN) seq_num:%d frag_index:%d flags:%02X\n",
|
|
this_fragmentation->fragment[begin_index].seq, begin_index,
|
|
this_fragmentation->fragment[begin_index].flags);
|
|
// should occur only after an "M_Offset out of range"
|
|
// The start sequence must be a begin sequence
|
|
this_fragmentation->start_index = (begin_index +1) & MAXFRAGNUM_MASK;
|
|
this_fragmentation->start_seq++;
|
|
return; // assembling frame failed
|
|
}
|
|
}
|
|
else
|
|
return; // assembling frame failed
|
|
|
|
// get the end of his frame
|
|
while (this_fragmentation->fragment[end_index].length)
|
|
{
|
|
if (this_fragmentation->fragment[end_index].flags & MP_END)
|
|
break;
|
|
|
|
end_index = (end_index +1) & MAXFRAGNUM_MASK;
|
|
|
|
if (end_index == this_fragmentation->start_index)
|
|
return; // assembling frame failed
|
|
}
|
|
|
|
// return if a lost fragment is found
|
|
if (!(this_fragmentation->fragment[end_index].length))
|
|
return; // assembling frame failed
|
|
|
|
// assemble the packet
|
|
//assemble frame, process it, reset fragmentation
|
|
uint16_t cur_len = 4; // This is set to 4 to leave 4 bytes for function processipin
|
|
|
|
LOG(4, s, t, "MPPP: processing fragments from %d to %d\n", begin_index, end_index);
|
|
// Push to the receive buffer
|
|
|
|
for (i = begin_index;; i = (i + 1) & MAXFRAGNUM_MASK)
|
|
{
|
|
this_frag = &this_fragmentation->fragment[i];
|
|
if(cur_len + this_frag->length > MAXETHER)
|
|
{
|
|
LOG(2, s, t, "MPPP: discarding reassembled frames larger than MAXETHER\n");
|
|
break;
|
|
}
|
|
|
|
memcpy(this_fragmentation->reassembled_frame+cur_len, this_frag->data, this_frag->length);
|
|
LOG(5, s, t, "MPPP: processing frame at %d, with len %d\n", i, this_frag->length);
|
|
|
|
cur_len += this_frag->length;
|
|
if (i == end_index)
|
|
{
|
|
this_fragmentation->re_frame_len = cur_len;
|
|
this_fragmentation->re_frame_begin_index = begin_index;
|
|
this_fragmentation->re_frame_end_index = end_index;
|
|
// Process the resassembled frame
|
|
LOG(5, s, t, "MPPP: Process the reassembled frame, len=%d\n",cur_len);
|
|
processmpframe(s, t, this_fragmentation->reassembled_frame, this_fragmentation->re_frame_len, 1);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Set reassembled frame length to zero after processing it
|
|
this_fragmentation->re_frame_len = 0;
|
|
for (i = begin_index;; i = (i + 1) & MAXFRAGNUM_MASK)
|
|
{
|
|
this_fragmentation->fragment[i].length = 0; // Indicates that this fragment has been consumed
|
|
this_fragmentation->fragment[i].flags = 0;
|
|
if (i == end_index)
|
|
break;
|
|
}
|
|
|
|
// Set the new start_index and start_seq
|
|
this_fragmentation->start_index = (end_index + 1) & MAXFRAGNUM_MASK;
|
|
this_fragmentation->start_seq = this_fragmentation->fragment[end_index].seq + 1;
|
|
LOG(4, s, t, "MPPP after assembling: start index is = %d, start seq=%d\n", this_fragmentation->start_index, this_fragmentation->start_seq);
|
|
|
|
begin_index = this_fragmentation->start_index;
|
|
if ((this_fragmentation->fragment[begin_index].length) &&
|
|
(this_fragmentation->fragment[begin_index].seq != this_fragmentation->start_seq))
|
|
{
|
|
LOG(3, this_fragmentation->fragment[begin_index].sid, this_fragmentation->fragment[begin_index].tid,
|
|
"MPPP: (START) seq_num:%d frag_index:%d flags:%02X is LOST\n",
|
|
this_fragmentation->fragment[begin_index].seq, begin_index,
|
|
this_fragmentation->fragment[begin_index].flags);
|
|
this_fragmentation->fragment[begin_index].length = 0;
|
|
this_fragmentation->fragment[begin_index].flags = 0;
|
|
}
|
|
|
|
if (this_fragmentation->start_seq <= Mmin)
|
|
// It's possible to find other complete frame or lost frame.
|
|
goto find_frame;
|
|
else if ((this_fragmentation->fragment[begin_index].length) &&
|
|
(this_fragmentation->fragment[begin_index].flags & MP_BEGIN))
|
|
// may be that the next frame is completed
|
|
goto assembling_frame;
|
|
|
|
return;
|
|
}
|
|
|
|
// process IPv6 packet received
|
|
//
|
|
// This MUST be called with at least 4 byte behind 'p'.
|
|
// (i.e. this routine writes to p[-4]).
|
|
void processipv6in(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l)
|
|
{
|
|
struct in6_addr ip;
|
|
in_addr_t ipv4;
|
|
|
|
CSTAT(processipv6in);
|
|
|
|
LOG_HEX(5, "IPv6", p, l);
|
|
|
|
ip = *(struct in6_addr *) (p + 8);
|
|
ipv4 = ntohl(*(uint32_t *)(p + 16));
|
|
|
|
if (l > MAXETHER)
|
|
{
|
|
LOG(1, s, t, "IP packet too long %d\n", l);
|
|
STAT(tunnel_rx_errors);
|
|
return ;
|
|
}
|
|
|
|
if (session[s].ppp.phase != Network || session[s].ppp.ipv6cp != Opened)
|
|
return;
|
|
|
|
// no spoof
|
|
if (session[s].ipv6address.s6_addr[0])
|
|
{
|
|
if ((sessionbyipv6new(ip) != s) &&
|
|
(ip.s6_addr[0] != 0xFE || ip.s6_addr[1] != 0x80 || ip.s6_addr16[1] != 0 || ip.s6_addr16[2] != 0 || ip.s6_addr16[3] != 0))
|
|
{
|
|
char str[INET6_ADDRSTRLEN];
|
|
LOG(5, s, t, "Dropping packet with spoofed IP %s\n",
|
|
inet_ntop(AF_INET6, &ip, str, INET6_ADDRSTRLEN));
|
|
return;
|
|
}
|
|
}
|
|
else if ((ipv4 != session[s].ip || memcmp(&config->ipv6_prefix, &ip, 8)) && sessionbyipv6(ip) != s)
|
|
{
|
|
char str[INET6_ADDRSTRLEN];
|
|
LOG(5, s, t, "Dropping packet with spoofed IP %s\n",
|
|
inet_ntop(AF_INET6, &ip, str, INET6_ADDRSTRLEN));
|
|
return;
|
|
}
|
|
|
|
// adjust MSS on SYN and SYN,ACK packets with options
|
|
if (p[6] == IPPROTO_TCP)
|
|
{
|
|
int ihl = 40; // length of IPv6 header
|
|
if (l >= ihl + 20 && (p[ihl + 13] & TCP_FLAG_SYN) && ((p[ihl + 12] >> 4) > 5))
|
|
adjust_tcp6_mss(s, t, p, l, p + ihl);
|
|
}
|
|
|
|
// Check if it's a Router Solicition message.
|
|
if (*(p + 6) == 58 && *(p + 7) == 255 && *(p + 24) == 0xFF && *(p + 25) == 2 &&
|
|
*(uint32_t *)(p + 26) == 0 && *(uint32_t *)(p + 30) == 0 &&
|
|
*(uint32_t *)(p + 34) == 0 &&
|
|
*(p + 38) == 0 && *(p + 39) == 2 && *(p + 40) == 133) {
|
|
LOG(3, s, t, "Got IPv6 RS\n");
|
|
send_ipv6_ra(s, t, &ip);
|
|
return;
|
|
}
|
|
|
|
// Check if DhcpV6, IP dst: FF02::1:2, Src Port 0x0222 (546), Dst Port 0x0223 (547)
|
|
if (*(p + 6) == 17 && *(p + 24) == 0xFF && *(p + 25) == 2 &&
|
|
*(uint32_t *)(p + 26) == 0 && *(uint32_t *)(p + 30) == 0 &&
|
|
*(uint16_t *)(p + 34) == 0 && *(p + 36) == 0 && *(p + 37) == 1 && *(p + 38) == 0 && *(p + 39) == 2 &&
|
|
*(p + 40) == 2 && *(p + 41) == 0x22 && *(p + 42) == 2 && *(p + 43) == 0x23)
|
|
{
|
|
dhcpv6_process_from_ipv6(s, t, p, l);
|
|
return;
|
|
}
|
|
|
|
// Add on the tun header
|
|
p -= 4;
|
|
*(uint32_t *) p = htonl(PKTIPV6);
|
|
l += 4;
|
|
|
|
// Are we throttled and a slave?
|
|
if (session[s].tbf_in && !config->cluster_iam_master) {
|
|
// Pass it to the master for handling.
|
|
master_throttle_packet(session[s].tbf_in, p, l);
|
|
return;
|
|
}
|
|
|
|
// Are we throttled and a master??
|
|
if (session[s].tbf_in && config->cluster_iam_master) {
|
|
// Actually handle the throttled packets.
|
|
tbf_queue_packet(session[s].tbf_in, p, l);
|
|
return;
|
|
}
|
|
|
|
// send to ethernet
|
|
if (tun_write(p, l) < 0)
|
|
{
|
|
STAT(tun_tx_errors);
|
|
LOG(0, s, t, "Error writing %d bytes to TUN device: %s (tunfd=%d, p=%p)\n",
|
|
l, strerror(errno), tunfd, p);
|
|
|
|
return;
|
|
}
|
|
|
|
p += 4;
|
|
l -= 4;
|
|
|
|
if (session[s].snoop_ip && session[s].snoop_port)
|
|
{
|
|
// Snooping this session
|
|
snoop_send_packet(p, l, session[s].snoop_ip, session[s].snoop_port);
|
|
}
|
|
|
|
update_sessions_in_stat(s, l);
|
|
|
|
eth_tx += l;
|
|
|
|
STAT(tun_tx_packets);
|
|
INC_STAT(tun_tx_bytes, l);
|
|
}
|
|
|
|
//
|
|
// Helper routine for the TBF filters.
|
|
// Used to send queued data in from the user.
|
|
//
|
|
void send_ipin(sessionidt s, uint8_t *buf, int len)
|
|
{
|
|
LOG_HEX(5, "IP in throttled", buf, len);
|
|
|
|
if (write(tunfd, buf, len) < 0)
|
|
{
|
|
STAT(tun_tx_errors);
|
|
LOG(0, 0, 0, "Error writing %d bytes to TUN device: %s (tunfd=%d, p=%p)\n",
|
|
len, strerror(errno), tunfd, buf);
|
|
|
|
return;
|
|
}
|
|
|
|
buf += 4;
|
|
len -= 4;
|
|
|
|
if (session[s].snoop_ip && session[s].snoop_port)
|
|
{
|
|
// Snooping this session
|
|
snoop_send_packet(buf, len, session[s].snoop_ip, session[s].snoop_port);
|
|
}
|
|
|
|
// Increment packet counters
|
|
update_session_in_stat(s, 1, len);
|
|
|
|
eth_tx += len;
|
|
|
|
STAT(tun_tx_packets);
|
|
INC_STAT(tun_tx_bytes, len - 4);
|
|
}
|
|
|
|
|
|
// Process CCP messages
|
|
void processccp(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l)
|
|
{
|
|
uint8_t b[MAXETHER];
|
|
uint8_t *q;
|
|
|
|
CSTAT(processccp);
|
|
|
|
LOG_HEX(5, "CCP", p, l);
|
|
|
|
if (session[s].ppp.phase < Network)
|
|
{
|
|
LOG(2, s, t, "CCP %s ignored in %s phase\n", ppp_code(*p), ppp_phase(session[s].ppp.phase));
|
|
return;
|
|
}
|
|
|
|
if (l < 1)
|
|
{
|
|
LOG(1, s, t, "Short CCP packet\n");
|
|
STAT(tunnel_rx_errors);
|
|
}
|
|
|
|
LOG(4, s, t, "CCP: recv %s\n", ppp_code(*p));
|
|
if (*p == ConfigAck)
|
|
{
|
|
switch (session[s].ppp.ccp)
|
|
{
|
|
case RequestSent:
|
|
initialise_restart_count(s, ccp);
|
|
change_state(s, ccp, AckReceived);
|
|
break;
|
|
|
|
case AckReceived:
|
|
case Opened:
|
|
LOG(2, s, t, "CCP: ConfigAck in state %s? Sending ConfigReq\n", ppp_state(session[s].ppp.ccp));
|
|
sendccp(s, t);
|
|
change_state(s, ccp, RequestSent);
|
|
break;
|
|
|
|
case AckSent:
|
|
LOG(3, s, t, "CCP: Opened\n");
|
|
change_state(s, ccp, Opened);
|
|
break;
|
|
|
|
default:
|
|
LOG(2, s, t, "CCP: ignoring %s in state %s\n", ppp_code(*p), ppp_state(session[s].ppp.ccp));
|
|
}
|
|
}
|
|
else if (*p == ConfigReq)
|
|
{
|
|
if (l < 6) // accept no compression
|
|
*p = ConfigAck;
|
|
else // compression requested--reject
|
|
*p = ConfigRej;
|
|
|
|
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, 0, 0, 0);
|
|
if (!q) return;
|
|
*q = TerminateAck;
|
|
*((uint16_t *) (q + 2)) = htons(l = 4);
|
|
break;
|
|
|
|
case Stopped:
|
|
initialise_restart_count(s, ccp);
|
|
sendccp(s, t);
|
|
if (*q == ConfigAck)
|
|
change_state(s, ccp, AckSent);
|
|
else
|
|
change_state(s, ccp, RequestSent);
|
|
|
|
break;
|
|
|
|
case RequestSent:
|
|
if (*q == ConfigAck)
|
|
change_state(s, ccp, AckSent);
|
|
|
|
break;
|
|
|
|
case AckReceived:
|
|
if (*q == ConfigAck)
|
|
change_state(s, ccp, Opened);
|
|
|
|
break;
|
|
|
|
case Opened:
|
|
initialise_restart_count(s, ccp);
|
|
sendccp(s, t);
|
|
/* fallthrough */
|
|
|
|
case AckSent:
|
|
if (*q == ConfigAck)
|
|
change_state(s, ccp, AckSent);
|
|
else
|
|
change_state(s, ccp, RequestSent);
|
|
|
|
break;
|
|
|
|
default:
|
|
LOG(2, s, t, "CCP: ignoring %s in state %s\n", ppp_code(*p), ppp_state(session[s].ppp.ccp));
|
|
return;
|
|
}
|
|
|
|
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, 0, 0, 0);
|
|
if (!q) return;
|
|
LOG(3, s, t, "CCP: send %s\n", ppp_code(*q));
|
|
tunnelsend(b, l + (q - b), t);
|
|
change_state(s, ccp, Stopped);
|
|
}
|
|
else if (*p != CodeRej)
|
|
{
|
|
ppp_code_rej(s, t, PPPCCP, "CCP", p, l, b, sizeof(b));
|
|
}
|
|
}
|
|
|
|
// send a CHAP challenge
|
|
void sendchap(sessionidt s, tunnelidt t)
|
|
{
|
|
uint8_t b[MAXETHER];
|
|
uint16_t r;
|
|
uint8_t *q;
|
|
|
|
CSTAT(sendchap);
|
|
|
|
r = radiusnew(s);
|
|
if (!r)
|
|
{
|
|
LOG(1, s, t, "No RADIUS to send challenge\n");
|
|
STAT(tunnel_tx_errors);
|
|
return;
|
|
}
|
|
|
|
LOG(1, s, t, "Send CHAP challenge\n");
|
|
|
|
radius[r].chap = 1; // CHAP not PAP
|
|
radius[r].id = ++sess_local[s].auth_id; // New ID
|
|
if (radius[r].state != RADIUSCHAP)
|
|
radius[r].try = 0;
|
|
|
|
radius[r].state = RADIUSCHAP;
|
|
radius[r].retry = backoff(radius[r].try++);
|
|
if (radius[r].try > 5)
|
|
{
|
|
sessionshutdown(s, "CHAP timeout.", CDN_ADMIN_DISC, TERM_REAUTHENTICATION_FAILURE);
|
|
STAT(tunnel_tx_errors);
|
|
return ;
|
|
}
|
|
q = makeppp(b, sizeof(b), 0, 0, s, t, PPPCHAP, 0, 0, 0);
|
|
if (!q) return;
|
|
|
|
*q = 1; // challenge
|
|
q[1] = radius[r].id; // ID
|
|
q[4] = 16; // value size (size of challenge)
|
|
memcpy(q + 5, radius[r].auth, 16); // challenge
|
|
memcpy(sess_local[s].auth_chall, radius[r].auth, 16);
|
|
strcpy((char *) q + 21, config->multi_n_hostname[tunnel[t].indexudp][0]?config->multi_n_hostname[tunnel[t].indexudp]:hostname); // our name
|
|
*(uint16_t *) (q + 2) = htons(strlen(config->multi_n_hostname[tunnel[t].indexudp][0]?config->multi_n_hostname[tunnel[t].indexudp]:hostname) + 21); // length
|
|
tunnelsend(b, strlen(config->multi_n_hostname[tunnel[t].indexudp][0]?config->multi_n_hostname[tunnel[t].indexudp]:hostname) + 21 + (q - b), t); // send it
|
|
}
|
|
|
|
// 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 prio, bundleidt bid, uint8_t mp_bits)
|
|
{
|
|
uint16_t hdr = 0x0002; // L2TP with no options
|
|
uint16_t type = mtype;
|
|
uint8_t *start = b;
|
|
|
|
if (t == TUNNEL_ID_PPPOE)
|
|
{
|
|
return pppoe_makeppp(b, size, p, l, s, t, mtype, prio, bid, mp_bits);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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;
|
|
|
|
// 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 (type < 0x100 && session[s].flags & SESSION_PFC)
|
|
{
|
|
*b++ = type;
|
|
}
|
|
else
|
|
{
|
|
*(uint16_t *) b = htons(type);
|
|
b += 2;
|
|
}
|
|
|
|
if (bid)
|
|
{
|
|
// 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 & 0x0FFF)|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=%td)\n", size, (b - start) + l);
|
|
return NULL;
|
|
}
|
|
|
|
// Copy the payload
|
|
if (p && l)
|
|
memcpy(b, p, l);
|
|
|
|
return b;
|
|
}
|
|
|
|
// fill in a L2TP message with a PPP frame,
|
|
// returns start of PPP frame
|
|
//(note: THIS ROUTINE CAN WRITES TO p[-28]).
|
|
uint8_t *opt_makeppp(uint8_t *p, int l, sessionidt s, tunnelidt t, uint16_t mtype, uint8_t prio, bundleidt bid, uint8_t mp_bits)
|
|
{
|
|
uint16_t hdr = 0x0002; // L2TP with no options
|
|
uint16_t type = mtype;
|
|
uint8_t *b = p;
|
|
|
|
if (t == TUNNEL_ID_PPPOE)
|
|
{
|
|
return opt_pppoe_makeppp(p, l, s, t, mtype, prio, bid, mp_bits);
|
|
}
|
|
|
|
// 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 (bid)
|
|
{
|
|
// Add the message type if this fragment has the begin bit set
|
|
if (mp_bits & MP_BEGIN)
|
|
{
|
|
b -= 2;
|
|
*(uint16_t *) b = htons(mtype); // Message type
|
|
}
|
|
|
|
// Set the sequence number and (B)egin (E)nd flags
|
|
if (session[s].mssf)
|
|
{
|
|
// Set the multilink bits
|
|
uint16_t bits_send = mp_bits;
|
|
b -= 2;
|
|
*(uint16_t *) b = htons((bundle[bid].seq_num_t & 0x0FFF)|bits_send);
|
|
}
|
|
else
|
|
{
|
|
b -= 4;
|
|
*(uint32_t *) b = htonl(bundle[bid].seq_num_t);
|
|
// Set the multilink bits
|
|
*b = mp_bits;
|
|
}
|
|
|
|
bundle[bid].seq_num_t++;
|
|
}
|
|
|
|
if (type < 0x100 && session[s].flags & SESSION_PFC)
|
|
{
|
|
b--;
|
|
*b = type;
|
|
}
|
|
else
|
|
{
|
|
b -= 2;
|
|
*(uint16_t *) b = htons(type);
|
|
}
|
|
|
|
if (type == PPPLCP || !(session[s].flags & SESSION_ACFC))
|
|
{
|
|
b -= 2;
|
|
*(uint16_t *) b = htons(0xFF03); // HDLC header
|
|
}
|
|
|
|
if (prio) hdr |= 0x0100; // set priority bit
|
|
b -= 2;
|
|
*(uint16_t *) b = htons(session[s].far); // session
|
|
b -= 2;
|
|
*(uint16_t *) b = htons(tunnel[t].far); // tunnel
|
|
b -= 2;
|
|
*(uint16_t *) b = htons(hdr);
|
|
|
|
return b;
|
|
}
|
|
|
|
static int add_lcp_auth(uint8_t *b, int size, int authtype)
|
|
{
|
|
int len = 0;
|
|
if ((authtype == AUTHCHAP && size < 5) || size < 4)
|
|
return 0;
|
|
|
|
*b++ = 3; // Authentication-Protocol
|
|
if (authtype == AUTHCHAP)
|
|
{
|
|
len = *b++ = 5; // length
|
|
*(uint16_t *) b = htons(PPPCHAP); b += 2;
|
|
*b++ = 5; // MD5
|
|
}
|
|
else if (authtype == AUTHPAP)
|
|
{
|
|
len = *b++ = 4; // length
|
|
*(uint16_t *) b = htons(PPPPAP); b += 2;
|
|
}
|
|
else
|
|
{
|
|
LOG(0, 0, 0, "add_lcp_auth called with unsupported auth type %d\n", authtype);
|
|
}
|
|
|
|
return len;
|
|
}
|
|
|
|
// Send LCP ConfigReq for MRU, authentication type and magic no
|
|
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, 0, 0, 0)))
|
|
return;
|
|
|
|
LOG(3, s, t, "LCP: send ConfigReq%s%s%s including MP options\n",
|
|
authtype ? " (" : "",
|
|
authtype ? (authtype == AUTHCHAP ? "CHAP" : "PAP") : "",
|
|
authtype ? ")" : "");
|
|
|
|
l = q;
|
|
*l++ = ConfigReq;
|
|
*l++ = ++sess_local[s].lcp_ident; // ID
|
|
|
|
l += 2; //Save space for length
|
|
|
|
if (sess_local[s].ppp_mru)
|
|
{
|
|
*l++ = 1; *l++ = 4; // Maximum-Receive-Unit (length 4)
|
|
*(uint16_t *) l = htons(sess_local[s].ppp_mru); l += 2;
|
|
}
|
|
|
|
if (authtype)
|
|
l += add_lcp_auth(l, sizeof(b) - (l - b), authtype);
|
|
|
|
if (session[s].magic)
|
|
{
|
|
*l++ = 5; *l++ = 6; // Magic-Number (length 6)
|
|
*(uint32_t *) l = htonl(session[s].magic);
|
|
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);
|
|
if (config->debug > 3) dumplcp(q, l - q);
|
|
|
|
if (l - (q + 4) <= sizeof(sess_local[s].lcp_last_sent_confreq))
|
|
{
|
|
memcpy(sess_local[s].lcp_last_sent_confreq, q + 4, l - (q + 4));
|
|
sess_local[s].lcp_last_sent_confreq_n = l - (q + 4);
|
|
}
|
|
else
|
|
{
|
|
LOG(3, s, t, "our LCP ConfigReq is too long for proxying\n");
|
|
dumplcp(q, l - q);
|
|
sess_local[s].lcp_last_sent_confreq_n = 0;
|
|
}
|
|
|
|
tunnelsend(b, (l - b), t);
|
|
restart_timer(s, lcp);
|
|
}
|
|
|
|
// Send CCP request for no compression
|
|
void sendccp(sessionidt s, tunnelidt t)
|
|
{
|
|
uint8_t b[500], *q;
|
|
|
|
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");
|
|
|
|
*q = ConfigReq;
|
|
*(q + 1) = ++sess_local[s].lcp_ident; // ID
|
|
*(uint16_t *)(q + 2) = htons(4); // Length
|
|
|
|
LOG_HEX(5, "PPPCCP", q, 4);
|
|
tunnelsend(b, (q - b) + 4 , t);
|
|
restart_timer(s, ccp);
|
|
}
|
|
|
|
// Reject unknown/unconfigured protocols
|
|
void protoreject(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l, uint16_t proto)
|
|
{
|
|
|
|
uint8_t buf[MAXETHER];
|
|
uint8_t *q;
|
|
int mru = session[s].mru;
|
|
if (mru < MINMTU) mru = MINMTU;
|
|
if (mru > sizeof(buf)) mru = sizeof(buf);
|
|
|
|
l += 6;
|
|
if (l > mru) l = mru;
|
|
|
|
q = makeppp(buf, sizeof(buf), 0, 0, s, t, PPPLCP, 0, 0, 0);
|
|
if (!q) return;
|
|
|
|
*q = ProtocolRej;
|
|
*(q + 1) = ++sess_local[s].lcp_ident;
|
|
*(uint16_t *)(q + 2) = htons(l);
|
|
*(uint16_t *)(q + 4) = htons(proto);
|
|
memcpy(q + 6, p, l - 6);
|
|
|
|
if (proto == PPPIPV6CP)
|
|
LOG(3, s, t, "LCP: send ProtocolRej (IPV6CP: not configured)\n");
|
|
else
|
|
LOG(2, s, t, "LCP: sent ProtocolRej (0x%04X: unsupported)\n", proto);
|
|
|
|
tunnelsend(buf, l + (q - buf), t);
|
|
}
|