+ 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
+ break;
+ case 8: // vendor name
+ memset(tunnel[t].vendor, 0, sizeof(tunnel[t].vendor));
+ memcpy(tunnel[t].vendor, b, (n < sizeof(tunnel[t].vendor)) ? n : sizeof(tunnel[t].vendor) - 1);
+ LOG(4, s, t, " Vendor name = \"%s\"\n", tunnel[t].vendor);
+ break;
+ case 9: // assigned tunnel
+ tunnel[t].far = ntohs(*(uint16_t *) (b));
+ LOG(4, s, t, " Remote tunnel id = %u\n", tunnel[t].far);
+ break;
+ case 10: // rx window
+ tunnel[t].window = ntohs(*(uint16_t *) (b));
+ if (!tunnel[t].window)
+ tunnel[t].window = 1; // window of 0 is silly
+ LOG(4, s, t, " rx window = %u\n", tunnel[t].window);
+ break;
+ case 11: // Request Challenge
+ {
+ LOG(4, s, t, " LAC requested CHAP authentication for tunnel\n");
+ if (message == 1)
+ build_chap_response(b, 2, n, &sendchalresponse);
+ else if (message == 2)
+ build_chap_response(b, 3, n, &sendchalresponse);
+ }
+ break;
+ case 13: // receive challenge Response
+ if (tunnel[t].isremotelns)
+ {
+ recvchalresponse = calloc(17, 1);
+ memcpy(recvchalresponse, b, (n < 17) ? n : 16);
+ LOG(3, s, t, "received challenge response from REMOTE LNS\n");
+ }
+ else
+ // Why did they send a response? We never challenge.
+ LOG(2, s, t, " received unexpected challenge response\n");
+ break;
+
+ case 14: // assigned session
+ asession = session[s].far = ntohs(*(uint16_t *) (b));
+ LOG(4, s, t, " assigned session = %u\n", asession);
+ break;
+ case 15: // call serial number
+ LOG(4, s, t, " call serial number = %u\n", ntohl(*(uint32_t *)b));
+ break;
+ case 18: // bearer type
+ LOG(4, s, t, " bearer type = %u\n", ntohl(*(uint32_t *)b));
+ // TBA - for RADIUS
+ break;
+ case 19: // framing type
+ LOG(4, s, t, " framing type = %u\n", ntohl(*(uint32_t *)b));
+ // TBA
+ break;
+ case 21: // called number
+ memset(called, 0, sizeof(called));
+ memcpy(called, b, (n < sizeof(called)) ? n : sizeof(called) - 1);
+ LOG(4, s, t, " Called <%s>\n", called);
+ break;
+ case 22: // calling number
+ memset(calling, 0, sizeof(calling));
+ memcpy(calling, b, (n < sizeof(calling)) ? n : sizeof(calling) - 1);
+ LOG(4, s, t, " Calling <%s>\n", calling);
+ break;
+ case 23: // subtype
+ break;
+ case 24: // tx connect speed
+ if (n == 4)
+ {
+ session[s].tx_connect_speed = ntohl(*(uint32_t *)b);
+ }
+ else
+ {
+ // AS5300s send connect speed as a string
+ char tmp[30];
+ memset(tmp, 0, sizeof(tmp));
+ memcpy(tmp, b, (n < sizeof(tmp)) ? n : sizeof(tmp) - 1);
+ session[s].tx_connect_speed = atol(tmp);
+ }
+ LOG(4, s, t, " TX connect speed <%u>\n", session[s].tx_connect_speed);
+ break;
+ case 38: // rx connect speed
+ if (n == 4)
+ {
+ session[s].rx_connect_speed = ntohl(*(uint32_t *)b);
+ }
+ else
+ {
+ // AS5300s send connect speed as a string
+ char tmp[30];
+ memset(tmp, 0, sizeof(tmp));
+ memcpy(tmp, b, (n < sizeof(tmp)) ? n : sizeof(tmp) - 1);
+ session[s].rx_connect_speed = atol(tmp);
+ }
+ LOG(4, s, t, " RX connect speed <%u>\n", session[s].rx_connect_speed);
+ break;
+ case 25: // Physical Channel ID
+ {
+ uint32_t tmp = ntohl(*(uint32_t *) b);
+ LOG(4, s, t, " Physical Channel ID <%X>\n", tmp);
+ break;
+ }
+ case 29: // Proxy Authentication Type
+ {
+ uint16_t atype = ntohs(*(uint16_t *)b);
+ LOG(4, s, t, " Proxy Auth Type %u (%s)\n", atype, ppp_auth_type(atype));
+ break;
+ }
+ case 30: // Proxy Authentication Name
+ {
+ char authname[64];
+ memset(authname, 0, sizeof(authname));
+ memcpy(authname, b, (n < sizeof(authname)) ? n : sizeof(authname) - 1);
+ LOG(4, s, t, " Proxy Auth Name (%s)\n",
+ authname);
+ break;
+ }
+ case 31: // Proxy Authentication Challenge
+ {
+ LOG(4, s, t, " Proxy Auth Challenge\n");
+ break;
+ }
+ case 32: // Proxy Authentication ID
+ {
+ uint16_t authid = ntohs(*(uint16_t *)(b));
+ LOG(4, s, t, " Proxy Auth ID (%u)\n", authid);
+ break;
+ }
+ case 33: // Proxy Authentication Response
+ LOG(4, s, t, " Proxy Auth Response\n");
+ break;
+ case 27: // last sent lcp
+ { // find magic number
+ uint8_t *p = b, *e = p + n;
+ while (p + 1 < e && p[1] && p + p[1] <= e)
+ {
+ if (*p == 5 && p[1] == 6) // Magic-Number
+ amagic = ntohl(*(uint32_t *) (p + 2));
+ else if (*p == 7) // Protocol-Field-Compression
+ aflags |= SESSION_PFC;
+ else if (*p == 8) // Address-and-Control-Field-Compression
+ aflags |= SESSION_ACFC;
+ p += p[1];
+ }
+ }
+ break;
+ case 28: // last recv lcp confreq
+ break;
+ case 26: // Initial Received LCP CONFREQ
+ break;
+ case 39: // seq required - we control it as an LNS anyway...
+ break;
+ case 36: // Random Vector
+ LOG(4, s, t, " Random Vector received. Enabled AVP Hiding.\n");
+ memset(session[s].random_vector, 0, sizeof(session[s].random_vector));
+ if (n > sizeof(session[s].random_vector))
+ n = sizeof(session[s].random_vector);
+ memcpy(session[s].random_vector, b, n);
+ session[s].random_vector_length = n;
+ break;
+ case 46: // ppp disconnect cause
+ if (n >= 5)
+ {
+ uint16_t code = ntohs(*(uint16_t *) b);
+ uint16_t proto = ntohs(*(uint16_t *) (b + 2));
+ uint8_t dir = *(b + 4);
+
+ LOG(4, s, t, " PPP disconnect cause "
+ "(code=%u, proto=%04X, dir=%u, msg=\"%.*s\")\n",
+ code, proto, dir, n - 5, b + 5);
+
+ disc_cause_set = mtype;
+
+ switch (code)
+ {
+ case 1: // admin disconnect
+ disc_cause = TERM_ADMIN_RESET;
+ disc_reason = "Administrative disconnect";
+ break;
+ case 3: // lcp terminate
+ if (dir != 2) break; // 1=peer (LNS), 2=local (LAC)
+ disc_cause = TERM_USER_REQUEST;
+ disc_reason = "Normal disconnection";
+ break;
+ case 4: // compulsory encryption unavailable
+ if (dir != 1) break; // 1=refused by peer, 2=local
+ disc_cause = TERM_USER_ERROR;
+ disc_reason = "Compulsory encryption refused";
+ break;
+ case 5: // lcp: fsm timeout
+ disc_cause = TERM_PORT_ERROR;
+ disc_reason = "LCP: FSM timeout";
+ break;
+ case 6: // lcp: no recognisable lcp packets received
+ disc_cause = TERM_PORT_ERROR;
+ disc_reason = "LCP: no recognisable LCP packets";
+ break;
+ case 7: // lcp: magic-no error (possibly looped back)
+ disc_cause = TERM_PORT_ERROR;
+ disc_reason = "LCP: magic-no error (possible loop)";
+ break;
+ case 8: // lcp: echo request timeout
+ disc_cause = TERM_PORT_ERROR;
+ disc_reason = "LCP: echo request timeout";
+ break;
+ case 13: // auth: fsm timeout
+ disc_cause = TERM_SERVICE_UNAVAILABLE;
+ disc_reason = "Authentication: FSM timeout";
+ break;
+ case 15: // auth: unacceptable auth protocol
+ disc_cause = TERM_SERVICE_UNAVAILABLE;
+ disc_reason = "Unacceptable authentication protocol";
+ break;
+ case 16: // auth: authentication failed
+ disc_cause = TERM_SERVICE_UNAVAILABLE;
+ disc_reason = "Authentication failed";
+ break;
+ case 17: // ncp: fsm timeout
+ disc_cause = TERM_SERVICE_UNAVAILABLE;
+ disc_reason = "NCP: FSM timeout";
+ break;
+ case 18: // ncp: no ncps available
+ disc_cause = TERM_SERVICE_UNAVAILABLE;
+ disc_reason = "NCP: no NCPs available";
+ break;
+ case 19: // ncp: failure to converge on acceptable address
+ disc_cause = TERM_SERVICE_UNAVAILABLE;
+ disc_reason = (dir == 1)
+ ? "NCP: too many Configure-Naks received from peer"
+ : "NCP: too many Configure-Naks sent to peer";
+ break;
+ case 20: // ncp: user not permitted to use any address
+ disc_cause = TERM_SERVICE_UNAVAILABLE;
+ disc_reason = (dir == 1)
+ ? "NCP: local link address not acceptable to peer"
+ : "NCP: remote link address not acceptable";
+ break;
+ }
+ }
+ break;
+ default:
+ {
+ static char e[] = "unknown AVP 0xXXXX";
+ LOG(2, s, t, " Unknown AVP type %u\n", mtype);
+ fatal = flags;
+ result = 2; // general error
+ error = 8; // unknown mandatory AVP
+ sprintf((msg = e) + 14, "%04x", mtype);
+ continue; // next
+ }
+ }
+ }
+ // process message
+ if (fatal & 0x80)
+ tunnelshutdown(t, "Invalid mandatory AVP", result, error, msg);
+ else
+ switch (message)
+ {
+ case 1: // SCCRQ - Start Control Connection Request
+ tunnel[t].state = TUNNELOPENING;
+ LOG(3, s, t, "Received SCCRQ\n");
+ if (main_quit != QUIT_SHUTDOWN)
+ {
+ LOG(3, s, t, "sending SCCRP\n");
+ controlt *c = controlnew(2); // sending SCCRP
+ control16(c, 2, version, 1); // protocol version
+ control32(c, 3, 3, 1); // framing
+ controls(c, 7, config->multi_n_hostname[tunnel[t].indexudp][0]?config->multi_n_hostname[tunnel[t].indexudp]:hostname, 1); // host name
+ if (sendchalresponse) controlb(c, 13, sendchalresponse, 16, 1); // Send Challenge response
+ control16(c, 9, t, 1); // assigned tunnel
+ controladd(c, 0, t); // send the resply
+ }
+ else
+ {
+ tunnelshutdown(t, "Shutting down", 6, 0, 0);
+ }
+ break;
+ case 2: // SCCRP
+ tunnel[t].state = TUNNELOPEN;
+ tunnel[t].lastrec = time_now;
+ LOG(3, s, t, "Received SCCRP\n");
+ if (main_quit != QUIT_SHUTDOWN)
+ {
+ if (tunnel[t].isremotelns && recvchalresponse)
+ {
+ hasht hash;
+
+ lac_calc_rlns_auth(t, 2, hash); // id = 2 (SCCRP)
+ // check authenticator
+ if (memcmp(hash, recvchalresponse, 16) == 0)
+ {
+ LOG(3, s, t, "sending SCCCN to REMOTE LNS\n");
+ controlt *c = controlnew(3); // sending SCCCN
+ controls(c, 7, config->multi_n_hostname[tunnel[t].indexudp][0]?config->multi_n_hostname[tunnel[t].indexudp]:hostname, 1); // host name
+ controls(c, 8, Vendor_name, 1); // Vendor name
+ control16(c, 2, version, 1); // protocol version
+ control32(c, 3, 3, 1); // framing Capabilities
+ if (sendchalresponse) controlb(c, 13, sendchalresponse, 16, 1); // Challenge response
+ control16(c, 9, t, 1); // assigned tunnel
+ controladd(c, 0, t); // send
+ }
+ else
+ {
+ tunnelshutdown(t, "Bad chap response from REMOTE LNS", 4, 0, 0);
+ }
+ }
+ }
+ else
+ {
+ tunnelshutdown(t, "Shutting down", 6, 0, 0);
+ }
+ break;
+ case 3: // SCCN
+ LOG(3, s, t, "Received SCCN\n");
+ tunnel[t].state = TUNNELOPEN;
+ tunnel[t].lastrec = time_now;
+ controlnull(t); // ack
+ break;
+ case 4: // StopCCN
+ LOG(3, s, t, "Received StopCCN\n");
+ controlnull(t); // ack
+ tunnelshutdown(t, "Stopped", 0, 0, 0); // Shut down cleanly
+ break;
+ case 6: // HELLO
+ LOG(3, s, t, "Received HELLO\n");
+ controlnull(t); // simply ACK
+ break;
+ case 7: // OCRQ
+ // TBA
+ LOG(3, s, t, "Received OCRQ\n");
+ break;
+ case 8: // OCRO
+ // TBA
+ LOG(3, s, t, "Received OCRO\n");
+ break;
+ case 9: // OCCN
+ // TBA
+ LOG(3, s, t, "Received OCCN\n");
+ break;
+ case 10: // ICRQ
+ LOG(3, s, t, "Received ICRQ\n");
+ if (sessionfree && main_quit != QUIT_SHUTDOWN)
+ {
+ controlt *c = controlnew(11); // ICRP
+
+ LOG(3, s, t, "Sending ICRP\n");
+
+ s = sessionfree;
+ sessionfree = session[s].next;
+ memset(&session[s], 0, sizeof(session[s]));
+
+ if (s > config->cluster_highest_sessionid)
+ config->cluster_highest_sessionid = s;
+
+ session[s].opened = time_now;
+ session[s].tunnel = t;
+ session[s].far = asession;
+ session[s].last_packet = session[s].last_data = time_now;
+ LOG(3, s, t, "New session (%u/%u)\n", tunnel[t].far, session[s].far);
+ control16(c, 14, s, 1); // assigned session
+ controladd(c, asession, t); // send the reply
+
+ strncpy(session[s].called, called, sizeof(session[s].called) - 1);
+ strncpy(session[s].calling, calling, sizeof(session[s].calling) - 1);
+
+ session[s].ppp.phase = Establish;
+ session[s].ppp.lcp = Starting;
+
+ STAT(session_created);
+ break;
+ }
+
+ {
+ controlt *c = controlnew(14); // CDN
+ LOG(3, s, t, "Sending CDN\n");
+ if (!sessionfree)
+ {
+ STAT(session_overflow);
+ LOG(1, 0, t, "No free sessions\n");
+ control16(c, 1, 4, 0); // temporary lack of resources
+ }
+ else
+ control16(c, 1, 2, 7); // shutting down, try another
+
+ controladd(c, asession, t); // send the message
+ }
+ return;
+ case 11: // ICRP
+ LOG(3, s, t, "Received ICRP\n");
+ if (session[s].forwardtosession)
+ {
+ controlt *c = controlnew(12); // ICCN
+
+ session[s].opened = time_now;
+ session[s].tunnel = t;
+ session[s].far = asession;
+ session[s].last_packet = session[s].last_data = time_now;
+
+ control32(c, 19, 1, 1); // Framing Type
+ control32(c, 24, 10000000, 1); // Tx Connect Speed
+ controladd(c, asession, t); // send the message
+ LOG(3, s, t, "Sending ICCN\n");
+ }
+ break;
+ case 12: // ICCN
+ LOG(3, s, t, "Received ICCN\n");
+ if (amagic == 0) amagic = time_now;
+ session[s].magic = amagic; // set magic number
+ session[s].flags = aflags; // set flags received
+ session[s].mru = PPPoE_MRU; // default
+ controlnull(t); // ack
+
+ // start LCP
+ sess_local[s].lcp_authtype = config->radius_authprefer;
+ sess_local[s].ppp_mru = MRU;
+
+ // Set multilink options before sending initial LCP packet
+ sess_local[s].mp_mrru = 1614;
+ sess_local[s].mp_epdis = ntohl(config->iftun_address ? config->iftun_address : my_address);
+
+ sendlcp(s, t);
+ change_state(s, lcp, RequestSent);
+ break;
+
+ case 14: // CDN
+ LOG(3, s, t, "Received CDN\n");
+ controlnull(t); // ack
+ sessionshutdown(s, disc_reason, CDN_NONE, disc_cause);
+ break;
+ case 0xFFFF:
+ LOG(1, s, t, "Missing message type\n");