+ }
+
+ // 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 < 5)
+ {
+ 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)
+ {
+ switch (*o)
+ {
+ case 3: // ip address
+ gotip++; // seen address
+ if (o[1] != 6 || o[1] > length) 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.", 3, 0);
+ return;
+ }
+ }
+
+ break;
+
+ case 129: // primary DNS
+ if (o[1] != 6 || o[1] > length) 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 || o[1] > length) 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);
+ 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);
+ 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)
+ {
+ *p = TerminateAck;
+ q = makeppp(b, sizeof(b), p, l, s, t, PPPIPCP);