+void lcp_open(sessionidt s, tunnelidt t)
+{
+ // transition to Authentication or Network phase:
+ session[s].ppp.phase = sess_local[s].lcp_authtype ? 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
+ {
+ // 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);
+ }
+}
+
+static void lcp_restart(sessionidt s)
+{
+ session[s].ppp.phase = Establish;
+ // This-Layer-Down
+ change_state(s, ipcp, Dead);
+ change_state(s, ipv6cp, Dead);
+ change_state(s, ccp, Dead);
+}
+
+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);
+ 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);
+ 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);
+ 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);
+}
+