+ udp_tx += len;
+}
+
+// process outgoing (to tunnel) IPv6
+//
+static void processipv6out(uint8_t * buf, int len)
+{
+ sessionidt s;
+ sessiont *sp;
+ tunnelidt t;
+ struct in6_addr ip6;
+
+ uint8_t *data = buf; // Keep a copy of the originals.
+ int size = len;
+
+ uint8_t b[MAXETHER + 20];
+
+ CSTAT(processipv6out);
+
+ if (len < MIN_IP_SIZE)
+ {
+ LOG(1, 0, 0, "Short IPv6, %d bytes\n", len);
+ STAT(tunnel_tx_errors);
+ return;
+ }
+ if (len >= MAXETHER)
+ {
+ LOG(1, 0, 0, "Oversize IPv6 packet %d bytes\n", len);
+ STAT(tunnel_tx_errors);
+ return;
+ }
+
+ // Skip the tun header
+ buf += 4;
+ len -= 4;
+
+ // Got an IP header now
+ if (*(uint8_t *)(buf) >> 4 != 6)
+ {
+ LOG(1, 0, 0, "IP: Don't understand anything except IPv6\n");
+ return;
+ }
+
+ ip6 = *(struct in6_addr *)(buf+24);
+ s = sessionbyipv6(ip6);
+
+ if (s == 0)
+ {
+ s = sessionbyipv6new(ip6);
+ }
+
+ if (s == 0)
+ {
+ // Is this a packet for a session that doesn't exist?
+ static int rate = 0; // Number of ICMP packets we've sent this second.
+ static int last = 0; // Last time we reset the ICMP packet counter 'rate'.
+
+ if (last != time_now)
+ {
+ last = time_now;
+ rate = 0;
+ }
+
+ if (rate++ < config->icmp_rate) // Only send a max of icmp_rate per second.
+ {
+ // FIXME: Should send icmp6 host unreachable
+ }
+ return;
+ }
+ if (session[s].bundle && bundle[session[s].bundle].num_of_links > 1)
+ {
+ bundleidt bid = session[s].bundle;
+ bundlet *b = &bundle[bid];
+
+ b->current_ses = (b->current_ses + 1) % b->num_of_links;
+ s = b->members[b->current_ses];
+ LOG(3, s, session[s].tunnel, "MPPP: Session number becomes: %u\n", s);
+ }
+ t = session[s].tunnel;
+ sp = &session[s];
+ sp->last_data = time_now;
+
+ // FIXME: add DoS prevention/filters?
+
+ if (sp->tbf_out)
+ {
+ // Are we throttling this session?
+ if (config->cluster_iam_master)
+ tbf_queue_packet(sp->tbf_out, data, size);
+ else
+ master_throttle_packet(sp->tbf_out, data, size);
+ return;
+ }
+ else if (sp->walled_garden && !config->cluster_iam_master)
+ {
+ // We are walled-gardening this
+ master_garden_packet(s, data, size);
+ 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, PPPIPV6, 0, 0, 0);
+ if (!p) return;
+ tunnelsend(b, len + (p-b), t); // send it...
+ }
+
+ // Snooping this session, send it to intercept box
+ if (sp->snoop_ip && sp->snoop_port)
+ snoop_send_packet(buf, len, sp->snoop_ip, sp->snoop_port);
+
+ increment_counter(&sp->cout, &sp->cout_wrap, len); // byte count
+ sp->cout_delta += len;
+ sp->pout++;
+ udp_tx += len;
+
+ sess_local[s].cout += len; // To send to master..
+ sess_local[s].pout++;
+}
+
+//
+// Helper routine for the TBF filters.
+// Used to send queued data in to the user!
+//
+static void send_ipout(sessionidt s, uint8_t *buf, int len)
+{
+ sessiont *sp;
+ tunnelidt t;
+ uint8_t *p;
+ uint8_t *data = buf; // Keep a copy of the originals.
+
+ uint8_t b[MAXETHER + 20];
+
+ if (len < 0 || len > MAXETHER)
+ {
+ LOG(1, 0, 0, "Odd size IP packet: %d bytes\n", len);
+ return;
+ }
+
+ // Skip the tun header
+ buf += 4;
+ len -= 4;
+
+ if (!session[s].ip)
+ return;
+
+ t = session[s].tunnel;
+ sp = &session[s];
+
+ LOG(5, s, t, "Ethernet -> Tunnel (%d bytes)\n", len);
+
+ // Add on L2TP header
+ if (*(uint16_t *) (data + 2) == htons(PKTIPV6))
+ p = makeppp(b, sizeof(b), buf, len, s, t, PPPIPV6, 0, 0, 0); // IPV6
+ else
+ p = makeppp(b, sizeof(b), buf, len, s, t, PPPIP, 0, 0, 0); // IPV4
+
+ if (!p) return;
+
+ tunnelsend(b, len + (p-b), t); // send it...
+
+ // Snooping this session.
+ if (sp->snoop_ip && sp->snoop_port)
+ snoop_send_packet(buf, len, sp->snoop_ip, sp->snoop_port);
+
+ increment_counter(&sp->cout, &sp->cout_wrap, len); // byte count
+ sp->cout_delta += len;
+ sp->pout++;
+ udp_tx += len;
+
+ sess_local[s].cout += len; // To send to master..
+ sess_local[s].pout++;
+}
+
+// add an AVP (16 bit)
+static void control16(controlt * c, uint16_t avp, uint16_t val, uint8_t m)
+{
+ uint16_t l = (m ? 0x8008 : 0x0008);
+ uint16_t *pint16 = (uint16_t *) (c->buf + c->length + 0);
+ pint16[0] = htons(l);
+ pint16[1] = htons(0);
+ pint16[2] = htons(avp);
+ pint16[3] = htons(val);
+ c->length += 8;
+}
+
+// add an AVP (32 bit)
+static void control32(controlt * c, uint16_t avp, uint32_t val, uint8_t m)
+{
+ uint16_t l = (m ? 0x800A : 0x000A);
+ uint16_t *pint16 = (uint16_t *) (c->buf + c->length + 0);
+ uint32_t *pint32 = (uint32_t *) (c->buf + c->length + 6);
+ pint16[0] = htons(l);
+ pint16[1] = htons(0);
+ pint16[2] = htons(avp);
+ pint32[0] = htonl(val);
+ c->length += 10;
+}
+
+// add an AVP (string)
+static void controls(controlt * c, uint16_t avp, char *val, uint8_t m)
+{
+ uint16_t l = ((m ? 0x8000 : 0) + strlen(val) + 6);
+ uint16_t *pint16 = (uint16_t *) (c->buf + c->length + 0);
+ pint16[0] = htons(l);
+ pint16[1] = htons(0);
+ pint16[2] = htons(avp);
+ memcpy(c->buf + c->length + 6, val, strlen(val));
+ c->length += 6 + strlen(val);
+}
+
+// add a binary AVP
+static void controlb(controlt * c, uint16_t avp, uint8_t *val, unsigned int len, uint8_t m)
+{
+ uint16_t l = ((m ? 0x8000 : 0) + len + 6);
+ uint16_t *pint16 = (uint16_t *) (c->buf + c->length + 0);
+ pint16[0] = htons(l);
+ pint16[1] = htons(0);
+ pint16[2] = htons(avp);
+ memcpy(c->buf + c->length + 6, val, len);
+ c->length += 6 + len;
+}
+
+// new control connection
+static controlt *controlnew(uint16_t mtype)
+{
+ controlt *c;
+ if (!controlfree)
+ c = malloc(sizeof(controlt));
+ else
+ {
+ c = controlfree;
+ controlfree = c->next;
+ }
+ assert(c);
+ c->next = 0;
+ c->buf[0] = 0xC8; // flags
+ c->buf[1] = 0x02; // ver
+ c->length = 12;
+ control16(c, 0, mtype, 1);
+ return c;
+}
+
+// send zero block if nothing is waiting
+// (ZLB send).
+static void controlnull(tunnelidt t)
+{
+ uint16_t buf[6];
+ if (tunnel[t].controlc) // Messages queued; They will carry the ack.
+ return;
+
+ buf[0] = htons(0xC802); // flags/ver
+ buf[1] = htons(12); // length
+ buf[2] = htons(tunnel[t].far); // tunnel
+ buf[3] = htons(0); // session
+ buf[4] = htons(tunnel[t].ns); // sequence
+ buf[5] = htons(tunnel[t].nr); // sequence
+ tunnelsend((uint8_t *)buf, 12, t);
+}
+
+// add a control message to a tunnel, and send if within window
+static void controladd(controlt *c, sessionidt far, tunnelidt t)
+{
+ uint16_t *pint16 = (uint16_t *) (c->buf + 2);
+ pint16[0] = htons(c->length); // length
+ pint16[1] = htons(tunnel[t].far); // tunnel
+ pint16[2] = htons(far); // session
+ pint16[3] = htons(tunnel[t].ns); // sequence
+ tunnel[t].ns++; // advance sequence
+ // link in message in to queue
+ if (tunnel[t].controlc)
+ tunnel[t].controle->next = c;
+ else
+ tunnel[t].controls = c;
+
+ tunnel[t].controle = c;
+ tunnel[t].controlc++;
+
+ // send now if space in window
+ if (tunnel[t].controlc <= tunnel[t].window)
+ {
+ tunnel[t].try = 0; // first send
+ tunnelsend(c->buf, c->length, t);
+ }
+}
+
+//
+// Throttle or Unthrottle a session
+//
+// Throttle the data from/to through a session to no more than
+// 'rate_in' kbit/sec in (from user) or 'rate_out' kbit/sec out (to
+// user).
+//
+// If either value is -1, the current value is retained for that
+// direction.
+//
+void throttle_session(sessionidt s, int rate_in, int rate_out)
+{
+ if (!session[s].opened)
+ return; // No-one home.
+
+ if (!*session[s].user)
+ return; // User not logged in
+
+ if (rate_in >= 0)
+ {
+ int bytes = rate_in * 1024 / 8; // kbits to bytes
+ if (session[s].tbf_in)
+ free_tbf(session[s].tbf_in);
+
+ if (rate_in > 0)
+ session[s].tbf_in = new_tbf(s, bytes * 2, bytes, send_ipin);
+ else
+ session[s].tbf_in = 0;
+
+ session[s].throttle_in = rate_in;
+ }
+
+ if (rate_out >= 0)
+ {
+ int bytes = rate_out * 1024 / 8;
+ if (session[s].tbf_out)
+ free_tbf(session[s].tbf_out);
+
+ if (rate_out > 0)
+ session[s].tbf_out = new_tbf(s, bytes * 2, bytes, send_ipout);
+ else
+ session[s].tbf_out = 0;
+
+ session[s].throttle_out = rate_out;
+ }
+}
+
+// add/remove filters from session (-1 = no change)
+void filter_session(sessionidt s, int filter_in, int filter_out)
+{
+ if (!session[s].opened)
+ return; // No-one home.
+
+ if (!*session[s].user)
+ return; // User not logged in
+
+ // paranoia
+ if (filter_in > MAXFILTER) filter_in = -1;
+ if (filter_out > MAXFILTER) filter_out = -1;
+ if (session[s].filter_in > MAXFILTER) session[s].filter_in = 0;
+ if (session[s].filter_out > MAXFILTER) session[s].filter_out = 0;
+
+ if (filter_in >= 0)
+ {
+ if (session[s].filter_in)
+ ip_filters[session[s].filter_in - 1].used--;
+
+ if (filter_in > 0)
+ ip_filters[filter_in - 1].used++;
+
+ session[s].filter_in = filter_in;
+ }
+
+ if (filter_out >= 0)
+ {
+ if (session[s].filter_out)
+ ip_filters[session[s].filter_out - 1].used--;
+
+ if (filter_out > 0)
+ ip_filters[filter_out - 1].used++;
+
+ session[s].filter_out = filter_out;
+ }
+}
+
+// start tidy shutdown of session
+void sessionshutdown(sessionidt s, char const *reason, int cdn_result, int cdn_error, int term_cause)
+{
+ int walled_garden = session[s].walled_garden;
+ bundleidt b = session[s].bundle;
+ //delete routes only for last session in bundle (in case of MPPP)
+ int del_routes = !b || (bundle[b].num_of_links == 1);
+
+ CSTAT(sessionshutdown);
+
+ if (!session[s].opened)
+ {
+ LOG(3, s, session[s].tunnel, "Called sessionshutdown on an unopened session.\n");
+ return; // not a live session
+ }
+
+ if (!session[s].die)
+ {
+ struct param_kill_session data = { &tunnel[session[s].tunnel], &session[s] };
+ LOG(2, s, session[s].tunnel, "Shutting down session %u: %s\n", s, reason);
+ run_plugins(PLUGIN_KILL_SESSION, &data);
+ }
+
+ if (session[s].ip && !walled_garden && !session[s].die)
+ {
+ // RADIUS Stop message
+ uint16_t r = radiusnew(s);
+ if (r)
+ {
+ // stop, if not already trying
+ if (radius[r].state != RADIUSSTOP)
+ {
+ radius[r].term_cause = term_cause;
+ radius[r].term_msg = reason;
+ radiussend(r, RADIUSSTOP);
+ }
+ }
+ else
+ LOG(1, s, session[s].tunnel, "No free RADIUS sessions for Stop message\n");
+
+ // Save counters to dump to accounting file
+ if (*config->accounting_dir && shut_acct_n < sizeof(shut_acct) / sizeof(*shut_acct))
+ memcpy(&shut_acct[shut_acct_n++], &session[s], sizeof(session[s]));
+ }
+
+ if (!session[s].die)
+ session[s].die = TIME + 150; // Clean up in 15 seconds
+
+ if (session[s].ip)
+ { // IP allocated, clear and unroute
+ int r;
+ int routed = 0;
+ for (r = 0; r < MAXROUTE && session[s].route[r].ip; r++)
+ {
+ if ((session[s].ip >> (32-session[s].route[r].prefixlen)) ==
+ (session[s].route[r].ip >> (32-session[s].route[r].prefixlen)))
+ routed++;
+
+ if (del_routes) routeset(s, session[s].route[r].ip, session[s].route[r].prefixlen, 0, 0);
+ session[s].route[r].ip = 0;
+ }
+
+ if (session[s].ip_pool_index == -1) // static ip
+ {
+ if (!routed && del_routes) routeset(s, session[s].ip, 0, 0, 0);
+ session[s].ip = 0;
+ }
+ else
+ free_ip_address(s);
+
+ // unroute IPv6, if setup
+ for (r = 0; r < MAXROUTE6 && session[s].route6[r].ipv6route.s6_addr[0] && session[s].route6[r].ipv6prefixlen; r++)
+ {
+ if (del_routes) route6set(s, session[s].route6[r].ipv6route, session[s].route6[r].ipv6prefixlen, 0);
+ memset(&session[s].route6[r], 0, sizeof(session[s].route6[r]));
+ }
+
+ if (session[s].ipv6address.s6_addr[0] && del_routes)
+ {
+ route6set(s, session[s].ipv6address, 128, 0);
+ }
+
+ if (b)
+ {
+ // This session was part of a bundle
+ bundle[b].num_of_links--;
+ LOG(3, s, session[s].tunnel, "MPPP: Dropping member link: %d from bundle %d\n",s,b);
+ if(bundle[b].num_of_links == 0)
+ {
+ bundleclear(b);
+ LOG(3, s, session[s].tunnel, "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, session[s].tunnel, "MPPP: Adjusted member links array\n");
+
+ // If the killed session is the first of the bundle,
+ // the new first session must be stored in the cache_ipmap
+ // else the function sessionbyip return 0 and the sending not work any more (processipout).
+ if (mem_num == 0)
+ {
+ sessionidt new_s = bundle[b].members[0];
+
+ routed = 0;
+ // Add the route for this session.
+ for (r = 0; r < MAXROUTE && session[new_s].route[r].ip; r++)
+ {
+ int i, prefixlen;
+ in_addr_t ip;
+
+ prefixlen = session[new_s].route[r].prefixlen;
+ ip = session[new_s].route[r].ip;
+
+ if (!prefixlen) prefixlen = 32;
+ ip &= 0xffffffff << (32 - prefixlen); // Force the ip to be the first one in the route.
+
+ for (i = ip; i < ip+(1<<(32-prefixlen)) ; ++i)
+ cache_ipmap(i, new_s);
+ }
+ cache_ipmap(session[new_s].ip, new_s);
+
+ // IPV6 route
+ for (r = 0; r < MAXROUTE6 && session[new_s].route6[r].ipv6prefixlen; r++)
+ {
+ cache_ipv6map(session[new_s].route6[r].ipv6route, session[new_s].route6[r].ipv6prefixlen, new_s);
+ }
+
+ if (session[new_s].ipv6address.s6_addr[0])
+ {
+ cache_ipv6map(session[new_s].ipv6address, 128, new_s);
+ }
+ }
+ }
+ }
+
+ cluster_send_bundle(b);
+ }
+ }
+
+ if (session[s].throttle_in || session[s].throttle_out) // Unthrottle if throttled.
+ throttle_session(s, 0, 0);
+
+ if (cdn_result)
+ {
+ if (session[s].tunnel == TUNNEL_ID_PPPOE)
+ {
+ pppoe_shutdown_session(s);
+ }
+ else
+ {
+ // Send CDN
+ controlt *c = controlnew(14); // sending CDN
+ if (cdn_error)
+ {
+ uint16_t buf[2];
+ buf[0] = htons(cdn_result);
+ buf[1] = htons(cdn_error);
+ controlb(c, 1, (uint8_t *)buf, 4, 1);
+ }
+ else
+ control16(c, 1, cdn_result, 1);
+
+ control16(c, 14, s, 1); // assigned session (our end)
+ controladd(c, session[s].far, session[s].tunnel); // send the message
+ }
+ }
+
+ // update filter refcounts
+ if (session[s].filter_in) ip_filters[session[s].filter_in - 1].used--;
+ if (session[s].filter_out) ip_filters[session[s].filter_out - 1].used--;
+
+ // clear PPP state
+ memset(&session[s].ppp, 0, sizeof(session[s].ppp));
+ sess_local[s].lcp.restart = 0;
+ sess_local[s].ipcp.restart = 0;
+ sess_local[s].ipv6cp.restart = 0;
+ sess_local[s].ccp.restart = 0;
+
+ cluster_send_session(s);
+}
+
+void sendipcp(sessionidt s, tunnelidt t)
+{
+ uint8_t buf[MAXETHER];
+ uint8_t *q;
+
+ CSTAT(sendipcp);
+ LOG(3, s, t, "IPCP: send ConfigReq\n");
+
+ if (!session[s].unique_id)
+ {
+ if (!++last_id) ++last_id; // skip zero
+ session[s].unique_id = last_id;
+ }
+
+ q = makeppp(buf, sizeof(buf), 0, 0, s, t, PPPIPCP, 0, 0, 0);
+ if (!q) return;
+
+ *q = ConfigReq;
+ q[1] = session[s].unique_id & 0xf; // ID, dont care, we only send one type of request
+ *(uint16_t *) (q + 2) = htons(10); // packet length
+ q[4] = 3; // ip address option
+ q[5] = 6; // option length
+ *(in_addr_t *) (q + 6) = config->peer_address ? config->peer_address :
+ config->iftun_n_address[tunnel[t].indexudp] ? config->iftun_n_address[tunnel[t].indexudp] :
+ my_address; // send my IP
+
+ tunnelsend(buf, 10 + (q - buf), t); // send it
+ restart_timer(s, ipcp);
+}
+
+void sendipv6cp(sessionidt s, tunnelidt t)
+{
+ uint8_t buf[MAXETHER];
+ uint8_t *q;
+
+ CSTAT(sendipv6cp);
+ LOG(3, s, t, "IPV6CP: send ConfigReq\n");
+
+ q = makeppp(buf, sizeof(buf), 0, 0, s, t, PPPIPV6CP, 0, 0, 0);
+ if (!q) return;
+
+ *q = ConfigReq;
+ q[1] = session[s].unique_id & 0xf; // ID, don't care, we
+ // only send one type
+ // of request
+ *(uint16_t *) (q + 2) = htons(14);
+ q[4] = 1; // interface identifier option
+ q[5] = 10; // option length
+ *(uint32_t *) (q + 6) = 0; // We'll be prefix::1
+ *(uint32_t *) (q + 10) = 0;
+ q[13] = 1;
+
+ tunnelsend(buf, 14 + (q - buf), t); // send it
+ restart_timer(s, ipv6cp);
+}
+
+static void sessionclear(sessionidt s)
+{
+ memset(&session[s], 0, sizeof(session[s]));
+ memset(&sess_local[s], 0, sizeof(sess_local[s]));
+ memset(&cli_session_actions[s], 0, sizeof(cli_session_actions[s]));
+
+ session[s].tunnel = T_FREE; // Mark it as free.
+ session[s].next = sessionfree;
+ sessionfree = s;
+}
+
+// kill a session now
+void sessionkill(sessionidt s, char *reason)
+{
+ CSTAT(sessionkill);
+
+ if (!session[s].opened) // not alive
+ return;
+
+ if (session[s].next)
+ {
+ LOG(0, s, session[s].tunnel, "Tried to kill a session with next pointer set (%u)\n", session[s].next);
+ return;
+ }
+
+ if (!session[s].die)
+ sessionshutdown(s, reason, CDN_ADMIN_DISC, TERM_ADMIN_RESET); // close radius/routes, etc.
+
+ if (sess_local[s].radius)
+ radiusclear(sess_local[s].radius, s); // cant send clean accounting data, session is killed
+
+ if (session[s].forwardtosession)
+ {
+ sessionidt sess = session[s].forwardtosession;
+ if (session[sess].forwardtosession == s)
+ {
+ // Shutdown the linked session also.
+ sessionshutdown(sess, reason, CDN_ADMIN_DISC, TERM_ADMIN_RESET);
+ }
+ }
+
+ LOG(2, s, session[s].tunnel, "Kill session %d (%s): %s\n", s, session[s].user, reason);
+ sessionclear(s);
+ cluster_send_session(s);
+}
+
+static void tunnelclear(tunnelidt t)
+{
+ if (!t) return;
+ memset(&tunnel[t], 0, sizeof(tunnel[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)
+{
+ sessionidt s;
+ controlt *c;
+
+ CSTAT(tunnelkill);
+
+ tunnel[t].state = TUNNELDIE;
+
+ // free control messages
+ while ((c = tunnel[t].controls))
+ {
+ controlt * n = c->next;
+ tunnel[t].controls = n;
+ tunnel[t].controlc--;
+ c->next = controlfree;
+ controlfree = c;
+ }
+ // kill sessions
+ for (s = 1; s <= config->cluster_highest_sessionid ; ++s)
+ if (session[s].tunnel == t)
+ sessionkill(s, reason);
+
+ // free tunnel
+ tunnelclear(t);
+ LOG(1, 0, t, "Kill tunnel %u: %s\n", t, reason);
+ cli_tunnel_actions[t].action = 0;
+ cluster_send_tunnel(t);
+}
+
+// shut down a tunnel cleanly
+static void tunnelshutdown(tunnelidt t, char *reason, int result, int error, char *msg)
+{
+ sessionidt s;
+
+ CSTAT(tunnelshutdown);
+
+ if (!tunnel[t].last || !tunnel[t].far || tunnel[t].state == TUNNELFREE)
+ {
+ // never set up, can immediately kill
+ tunnelkill(t, reason);
+ return;
+ }
+ LOG(1, 0, t, "Shutting down tunnel %u (%s)\n", t, reason);
+
+ // close session
+ for (s = 1; s <= config->cluster_highest_sessionid ; ++s)
+ if (session[s].tunnel == t)
+ sessionshutdown(s, reason, CDN_NONE, TERM_ADMIN_RESET);
+
+ tunnel[t].state = TUNNELDIE;
+ tunnel[t].die = TIME + 700; // Clean up in 70 seconds
+ cluster_send_tunnel(t);
+ // TBA - should we wait for sessions to stop?
+ if (result)
+ {
+ controlt *c = controlnew(4); // sending StopCCN
+ if (error)
+ {
+ uint16_t buf[32];
+ int l = 4;
+ buf[0] = htons(result);
+ buf[1] = htons(error);
+ if (msg)
+ {
+ int m = strlen(msg);
+ if (m + 4 > sizeof(buf))
+ m = sizeof(buf) - 4;
+
+ memcpy(buf+2, msg, m);
+ l += m;
+ }
+
+ controlb(c, 1, (uint8_t *)buf, l, 1);
+ }
+ else
+ control16(c, 1, result, 1);
+
+ control16(c, 9, t, 1); // assigned tunnel (our end)
+ controladd(c, 0, t); // send the message
+ }
+}
+
+// read and process packet on tunnel (UDP)
+void processudp(uint8_t *buf, int len, struct sockaddr_in *addr, uint16_t indexudpfd)
+{
+ uint8_t *sendchalresponse = NULL;
+ uint8_t *recvchalresponse = NULL;
+ uint16_t l = len, t = 0, s = 0, ns = 0, nr = 0;
+ uint8_t *p = buf + 2;
+
+
+ CSTAT(processudp);
+
+ udp_rx += len;
+ udp_rx_pkt++;
+ LOG_HEX(5, "UDP Data", buf, len);
+ STAT(tunnel_rx_packets);
+ INC_STAT(tunnel_rx_bytes, len);
+ if (len < 6)
+ {
+ LOG(1, 0, 0, "Short UDP, %d bytes\n", len);
+ STAT(tunnel_rx_errors);
+ return;
+ }
+ if ((buf[1] & 0x0F) != 2)
+ {
+ LOG(1, 0, 0, "Bad L2TP ver %d\n", buf[1] & 0x0F);
+ STAT(tunnel_rx_errors);
+ return;
+ }
+ if (*buf & 0x40)
+ { // length
+ l = ntohs(*(uint16_t *) p);
+ p += 2;
+ }
+ t = ntohs(*(uint16_t *) p);
+ p += 2;
+ s = ntohs(*(uint16_t *) p);
+ p += 2;
+ if (s >= MAXSESSION)
+ {
+ LOG(1, s, t, "Received UDP packet with invalid session ID\n");
+ STAT(tunnel_rx_errors);
+ return;
+ }
+ if (t >= MAXTUNNEL)
+ {
+ LOG(1, s, t, "Received UDP packet with invalid tunnel ID\n");
+ STAT(tunnel_rx_errors);
+ return;
+ }
+ if (t == TUNNEL_ID_PPPOE)
+ {
+ LOG(1, s, t, "Received UDP packet with tunnel ID reserved for pppoe\n");
+ STAT(tunnel_rx_errors);
+ return;
+ }
+ if (*buf & 0x08)
+ { // ns/nr
+ ns = ntohs(*(uint16_t *) p);
+ p += 2;
+ nr = ntohs(*(uint16_t *) p);
+ p += 2;
+ }
+ if (*buf & 0x02)
+ { // offset
+ uint16_t o = ntohs(*(uint16_t *) p);
+ p += o + 2;
+ }
+ if ((p - buf) > l)
+ {
+ LOG(1, s, t, "Bad length %d>%d\n", (int) (p - buf), l);
+ STAT(tunnel_rx_errors);
+ return;
+ }
+ l -= (p - buf);
+
+ // used to time out old tunnels
+ if (t && tunnel[t].state == TUNNELOPEN)
+ tunnel[t].lastrec = time_now;
+
+ if (*buf & 0x80)
+ { // control
+ uint16_t message = 0xFFFF; // message type
+ uint8_t fatal = 0;
+ uint8_t mandatory = 0;
+ uint16_t asession = 0; // assigned session
+ uint32_t amagic = 0; // magic number
+ uint8_t aflags = 0; // flags from last LCF
+ uint16_t version = 0x0100; // protocol version (we handle 0.0 as well and send that back just in case)
+ char called[MAXTEL] = ""; // called number
+ char calling[MAXTEL] = ""; // calling number
+
+ if (!config->cluster_iam_master)
+ {
+ master_forward_packet(buf, len, addr->sin_addr.s_addr, addr->sin_port, indexudpfd);
+ return;
+ }
+
+ // control messages must have bits 0x80|0x40|0x08
+ // (type, length and sequence) set, and bits 0x02|0x01
+ // (offset and priority) clear
+ if ((*buf & 0xCB) != 0xC8)
+ {
+ LOG(1, s, t, "Bad control header %02X\n", *buf);
+ STAT(tunnel_rx_errors);
+ return;
+ }
+
+ // check for duplicate tunnel open message
+ if (!t && ns == 0)
+ {
+ int i;
+
+ //
+ // Is this a duplicate of the first packet? (SCCRQ)
+ //
+ for (i = 1; i <= config->cluster_highest_tunnelid ; ++i)
+ {
+ if (tunnel[i].state != TUNNELOPENING ||
+ tunnel[i].ip != ntohl(*(in_addr_t *) & addr->sin_addr) ||
+ tunnel[i].port != ntohs(addr->sin_port) )
+ continue;
+ t = i;
+ LOG(3, s, t, "Duplicate SCCRQ?\n");
+ break;
+ }
+ }
+
+ LOG(3, s, t, "Control message (%d bytes): (unacked %d) l-ns %u l-nr %u r-ns %u r-nr %u\n",
+ l, tunnel[t].controlc, tunnel[t].ns, tunnel[t].nr, ns, nr);
+
+ // if no tunnel specified, assign one
+ if (!t)
+ {
+ if (!(t = new_tunnel()))
+ {
+ LOG(1, 0, 0, "No more tunnels\n");
+ STAT(tunnel_overflow);
+ return;
+ }
+ tunnelclear(t);
+ tunnel[t].ip = ntohl(*(in_addr_t *) & addr->sin_addr);
+ tunnel[t].port = ntohs(addr->sin_port);
+ tunnel[t].window = 4; // default window
+ tunnel[t].indexudp = indexudpfd;
+ STAT(tunnel_created);
+ LOG(1, 0, t, " New tunnel from %s:%u ID %u\n",
+ fmtaddr(htonl(tunnel[t].ip), 0), tunnel[t].port, t);
+ }
+
+ // If the 'ns' just received is not the 'nr' we're
+ // expecting, just send an ack and drop it.
+ //
+ // if 'ns' is less, then we got a retransmitted packet.
+ // if 'ns' is greater than missed a packet. Either way
+ // we should ignore it.
+ if (ns != tunnel[t].nr)
+ {
+ // is this the sequence we were expecting?
+ STAT(tunnel_rx_errors);
+ LOG(1, 0, t, " Out of sequence tunnel %u, (%u is not the expected %u)\n",
+ t, ns, tunnel[t].nr);
+
+ if (l) // Is this not a ZLB?
+ controlnull(t);
+ return;
+ }
+
+ // check sequence of this message
+ {
+ int skip = tunnel[t].window; // track how many in-window packets are still in queue
+ // some to clear maybe?
+ while (tunnel[t].controlc > 0 && (((tunnel[t].ns - tunnel[t].controlc) - nr) & 0x8000))
+ {
+ controlt *c = tunnel[t].controls;
+ tunnel[t].controls = c->next;
+ tunnel[t].controlc--;
+ c->next = controlfree;
+ controlfree = c;
+ skip--;
+ tunnel[t].try = 0; // we have progress
+ }
+
+ // receiver advance (do here so quoted correctly in any sends below)
+ if (l) tunnel[t].nr = (ns + 1);
+ if (skip < 0) skip = 0;
+ if (skip < tunnel[t].controlc)
+ {
+ // some control packets can now be sent that were previous stuck out of window
+ int tosend = tunnel[t].window - skip;
+ controlt *c = tunnel[t].controls;
+ while (c && skip)
+ {
+ c = c->next;
+ skip--;
+ }
+ while (c && tosend)
+ {
+ tunnel[t].try = 0; // first send
+ tunnelsend(c->buf, c->length, t);
+ c = c->next;
+ tosend--;
+ }
+ }
+ if (!tunnel[t].controlc)
+ tunnel[t].retry = 0; // caught up
+ }
+ if (l)
+ { // if not a null message
+ int result = 0;
+ int error = 0;
+ char *msg = 0;
+
+ // Default disconnect cause/message on receipt of CDN. Set to
+ // more specific value from attribute 1 (result code) or 46
+ // (disconnect cause) if present below.
+ int disc_cause_set = 0;
+ int disc_cause = TERM_NAS_REQUEST;
+ char const *disc_reason = "Closed (Received CDN).";
+
+ // process AVPs
+ while (l && !(fatal & 0x80)) // 0x80 = mandatory AVP
+ {
+ uint16_t n = (ntohs(*(uint16_t *) p) & 0x3FF);
+ uint8_t *b = p;
+ uint8_t flags = *p;
+ uint16_t mtype;
+
+ if (n > l)
+ {
+ LOG(1, s, t, "Invalid length in AVP\n");
+ STAT(tunnel_rx_errors);
+ return;
+ }
+ p += n; // next
+ l -= n;
+ if (flags & 0x3C) // reserved bits, should be clear
+ {
+ LOG(1, s, t, "Unrecognised AVP flags %02X\n", *b);
+ fatal = flags;
+ result = 2; // general error
+ error = 3; // reserved field non-zero
+ msg = 0;
+ continue; // next
+ }
+ b += 2;
+ if (*(uint16_t *) (b))
+ {
+ LOG(2, s, t, "Unknown AVP vendor %u\n", ntohs(*(uint16_t *) (b)));
+ fatal = flags;
+ result = 2; // general error
+ error = 6; // generic vendor-specific error
+ msg = "unsupported vendor-specific";
+ continue; // next
+ }
+ b += 2;
+ mtype = ntohs(*(uint16_t *) (b));
+ b += 2;
+ n -= 6;
+
+ if (flags & 0x40)
+ {
+ uint16_t orig_len;
+
+ // handle hidden AVPs
+ if (!*config->l2tp_secret)
+ {
+ LOG(1, s, t, "Hidden AVP requested, but no L2TP secret.\n");
+ fatal = flags;
+ result = 2; // general error
+ error = 6; // generic vendor-specific error
+ msg = "secret not specified";
+ continue;
+ }
+ if (!session[s].random_vector_length)
+ {
+ LOG(1, s, t, "Hidden AVP requested, but no random vector.\n");
+ fatal = flags;
+ result = 2; // general error
+ error = 6; // generic
+ msg = "no random vector";
+ continue;
+ }
+ if (n < 8)
+ {
+ LOG(2, s, t, "Short hidden AVP.\n");
+ fatal = flags;
+ result = 2; // general error
+ error = 2; // length is wrong
+ msg = 0;
+ continue;
+ }
+
+ // Unhide the AVP
+ unhide_value(b, n, mtype, session[s].random_vector, session[s].random_vector_length);
+
+ orig_len = ntohs(*(uint16_t *) b);
+ if (orig_len > n + 2)
+ {
+ LOG(1, s, t, "Original length %d too long in hidden AVP of length %d; wrong secret?\n",
+ orig_len, n);
+
+ fatal = flags;
+ result = 2; // general error
+ error = 2; // length is wrong
+ msg = 0;
+ continue;
+ }
+
+ b += 2;
+ n = orig_len;
+ }
+
+ LOG(4, s, t, " AVP %u (%s) len %d%s%s\n", mtype, l2tp_avp_name(mtype), n,
+ flags & 0x40 ? ", hidden" : "", flags & 0x80 ? ", mandatory" : "");
+
+ switch (mtype)
+ {
+ case 0: // message type
+ message = ntohs(*(uint16_t *) b);
+ mandatory = flags & 0x80;
+ LOG(4, s, t, " Message type = %u (%s)\n", message, l2tp_code(message));
+ break;
+ case 1: // result code
+ {
+ uint16_t rescode = ntohs(*(uint16_t *) b);
+ char const *resdesc = "(unknown)";
+ char const *errdesc = NULL;
+ int cause = 0;
+
+ if (message == 4)
+ { /* StopCCN */
+ resdesc = l2tp_stopccn_result_code(rescode);
+ cause = TERM_LOST_SERVICE;
+ }
+ else if (message == 14)
+ { /* CDN */
+ resdesc = l2tp_cdn_result_code(rescode);
+ if (rescode == 1)
+ cause = TERM_LOST_CARRIER;
+ else
+ cause = TERM_ADMIN_RESET;
+ }
+
+ LOG(4, s, t, " Result Code %u: %s\n", rescode, resdesc);
+ if (n >= 4)
+ {
+ uint16_t errcode = ntohs(*(uint16_t *)(b + 2));
+ errdesc = l2tp_error_code(errcode);
+ LOG(4, s, t, " Error Code %u: %s\n", errcode, errdesc);
+ }
+ if (n > 4)
+ LOG(4, s, t, " Error String: %.*s\n", n-4, b+4);
+
+ if (cause && disc_cause_set < mtype) // take cause from attrib 46 in preference
+ {
+ disc_cause_set = mtype;
+ disc_reason = errdesc ? errdesc : resdesc;
+ disc_cause = cause;
+ }
+
+ break;
+ }
+ break;
+ case 2: // protocol version
+ {
+ version = ntohs(*(uint16_t *) (b));
+ LOG(4, s, t, " Protocol version = %u\n", version);
+ if (version && version != 0x0100)
+ { // allow 0.0 and 1.0
+ LOG(1, s, t, " Bad protocol version %04X\n", version);
+ fatal = flags;
+ result = 5; // unspported protocol version
+ error = 0x0100; // supported version
+ msg = 0;
+ continue; // next
+ }
+ }
+ break;
+ case 3: // framing capabilities
+ break;
+ case 4: // bearer capabilities
+ break;
+ case 5: // tie breaker
+ // We never open tunnels, so we don't care about tie breakers
+ continue;
+ case 6: // firmware revision
+ break;
+ case 7: // host name
+ memset(tunnel[t].hostname, 0, sizeof(tunnel[t].hostname));
+ memcpy(tunnel[t].hostname, b, (n < sizeof(tunnel[t].hostname)) ? n : sizeof(tunnel[t].hostname) - 1);
+ LOG(4, s, t, " Tunnel hostname = \"%s\"\n", tunnel[t].hostname);
+ // TBA - to send to RADIUS