+ 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
+ session[s].mru = ntohs(*(uint16_t *)(o + 2));
+ LOG(3, s, t, " Remote requested MRU of %u\n", session[s].mru);
+ break;
+
+ case 3: // Authentication-Protocol
+ if (authtype > 0)
+ break;
+
+ {
+ int 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 && *(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);
+ }
+ }
+
+ break;
+
+ default:
+ LOG(2, s, t, " Remote NAKed LCP type %u?\n", type);
+ break;
+ }
+ x -= length;
+ o += length;
+ }
+
+ if (!authtype)
+ {
+ sessionshutdown(s, "Unsupported authentication.", 3, 0);
+ 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);
+ if (!response) return;
+ *response = TerminateAck;
+ *((uint16_t *) (response + 2)) = htons(l = 4);
+ tunnelsend(b, l + (response - b), t);
+ }
+ break;
+
+ case RequestSent:
+ case AckSent:
+ initialise_restart_count(s, lcp);
+ sendlcp(s, t, sess_local[s].lcp_authtype);
+ break;
+
+ case AckReceived:
+ LOG(2, s, t, "LCP: ConfigNak in state %s? Sending ConfigReq\n", ppp_state(session[s].ppp.lcp));
+ sendlcp(s, t, sess_local[s].lcp_authtype);
+ break;
+
+ case Opened:
+ lcp_restart(s);
+ sendlcp(s, t, sess_local[s].lcp_authtype);
+ 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)
+ {
+ LOG(3, s, t, "LCP: Received TerminateReq. Sending TerminateAck\n");
+ *p = TerminateAck; // close
+ q = makeppp(b, sizeof(b), p, l, s, t, PPPLCP);
+ if (!q) return;
+ tunnelsend(b, l + (q - b), t); // send it
+ sessionshutdown(s, "Remote end closed connection.", 3, 0);
+ }
+ else if (*p == TerminateAck)
+ {
+ sessionshutdown(s, "Connection closed.", 3, 0);
+ }
+ else if (*p == ProtocolRej)
+ {
+ if (*(uint16_t *) (p+4) == htons(PPPIPV6CP))
+ {
+ LOG(3, s, t, "IPv6 rejected\n");
+ change_state(s, ipv6cp, Closed);
+ }
+ else
+ {
+ LOG(1, s, t, "Unexpected LCP protocol reject 0x%X\n",
+ ntohs(*(uint16_t *) (p+4)));
+ STAT(tunnel_rx_errors);
+ }
+ }
+ else if (*p == EchoReq)
+ {
+ LOG(5, s, t, "LCP: Received EchoReq. Sending EchoReply\n");
+ *p = EchoReply; // reply
+ *(uint32_t *) (p + 4) = htonl(session[s].magic); // our magic number
+ q = makeppp(b, sizeof(b), p, l, s, t, PPPLCP);
+ if (!q) return;
+ tunnelsend(b, l + (q - b), t); // send it
+ }
+ else if (*p == EchoReply)
+ {
+ // Ignore it, last_packet time is set earlier than this.
+ }
+ else
+ {
+ int code = *p;
+ int mru = session[s].mru;
+ if (!mru)
+ mru = DEFAULT_MRU;
+
+ if (l > mru) l = mru;
+
+ *p = CodeRej;
+ q = makeppp(b, sizeof(b), p, l, s, t, PPPLCP);
+ if (!q) return;
+
+ LOG(3, s, t, "Unexpected LCP code %s\n", ppp_code(code));
+ tunnelsend(b, l + (q - b), t);
+ }
+}
+
+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)
+ {
+ uint16_t r = radiusnew(s);
+ if (r)
+ radiussend(r, RADIUSSTART); // send radius start
+ }
+
+ // 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[MAXCONTROL];
+ uint8_t *q = 0;
+ uint16_t hl;
+
+ CSTAT(processipcp);
+
+ LOG_HEX(5, "IPCP", p, l);
+ if (l < 5)
+ {
+ LOG(1, s, t, "Short IPCP %d bytes\n", l);
+ STAT(tunnel_rx_errors);