static int pptpd_listener_start(pptpd_listener *_this) { int sock, ival, sock_gre; struct sockaddr_in bind_sin, bind_sin_gre; int wildcardbinding; #ifdef NPPPD_FAKEBIND extern void set_faith(int, int); #endif wildcardbinding = (_this->bind_sin.sin_addr.s_addr == INADDR_ANY)? 1 : 0; sock = -1; sock_gre = -1; memcpy(&bind_sin, &_this->bind_sin, sizeof(bind_sin)); memcpy(&bind_sin_gre, &_this->bind_sin_gre, sizeof(bind_sin_gre)); if (_this->phy_label[0] == '\0') strlcpy(_this->phy_label, PPTPD_DEFAULT_LAYER2_LABEL, sizeof(_this->phy_label)); if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) { pptpd_log(_this->self, LOG_ERR, "socket() failed at %s(): %m", __func__); goto fail; } ival = 1; if(setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, &ival, sizeof(ival)) < 0){ pptpd_log(_this->self, LOG_WARNING, "setsockopt(SO_REUSEPORT) failed at %s(): %m", __func__); } #ifdef NPPPD_FAKEBIND if (!wildcardbinding) set_faith(sock, 1); #endif #if defined(IP_STRICT_RCVIF) && defined(USE_STRICT_RCVIF) ival = 1; if (setsockopt(sock, IPPROTO_IP, IP_STRICT_RCVIF, &ival, sizeof(ival)) != 0) pptpd_log(_this->self, LOG_WARNING, "%s(): setsockopt(IP_STRICT_RCVIF) failed: %m", __func__); #endif if ((ival = fcntl(sock, F_GETFL, 0)) < 0) { pptpd_log(_this->self, LOG_ERR, "fcntl(F_GET_FL) failed at %s(): %m", __func__); goto fail; } else if (fcntl(sock, F_SETFL, ival | O_NONBLOCK) < 0) { pptpd_log(_this->self, LOG_ERR, "fcntl(F_SET_FL) failed at %s(): %m", __func__); goto fail; } if (bind(sock, (struct sockaddr *)&_this->bind_sin, _this->bind_sin.sin_len) != 0) { pptpd_log(_this->self, LOG_ERR, "bind(%s:%u) failed at %s(): %m", inet_ntoa(_this->bind_sin.sin_addr), ntohs(_this->bind_sin.sin_port), __func__); goto fail; } if (listen(sock, PPTP_BACKLOG) != 0) { pptpd_log(_this->self, LOG_ERR, "listen(%s:%u) failed at %s(): %m", inet_ntoa(_this->bind_sin.sin_addr), ntohs(_this->bind_sin.sin_port), __func__); goto fail; } #ifdef NPPPD_FAKEBIND if (!wildcardbinding) set_faith(sock, 0); #endif pptpd_log(_this->self, LOG_INFO, "Listening %s:%u/tcp (PPTP PAC) [%s]", inet_ntoa(_this->bind_sin.sin_addr), ntohs(_this->bind_sin.sin_port), _this->phy_label); /* GRE */ bind_sin_gre.sin_port = 0; if ((sock_gre = priv_socket(AF_INET, SOCK_RAW, IPPROTO_GRE)) < 0) { pptpd_log(_this->self, LOG_ERR, "socket() failed at %s(): %m", __func__); goto fail; } #ifdef NPPPD_FAKEBIND if (!wildcardbinding) set_faith(sock_gre, 1); #endif #if defined(IP_STRICT_RCVIF) && defined(USE_STRICT_RCVIF) ival = 1; if (setsockopt(sock_gre, IPPROTO_IP, IP_STRICT_RCVIF, &ival, sizeof(ival)) != 0) pptpd_log(_this->self, LOG_WARNING, "%s(): setsockopt(IP_STRICT_RCVIF) failed: %m", __func__); #endif #ifdef IP_PIPEX ival = 1; if (setsockopt(sock_gre, IPPROTO_IP, IP_PIPEX, &ival, sizeof(ival)) != 0) pptpd_log(_this->self, LOG_WARNING, "%s(): setsockopt(IP_PIPEX) failed: %m", __func__); #endif if ((ival = fcntl(sock_gre, F_GETFL, 0)) < 0) { pptpd_log(_this->self, LOG_ERR, "fcntl(F_GET_FL) failed at %s(): %m", __func__); goto fail; } else if (fcntl(sock_gre, F_SETFL, ival | O_NONBLOCK) < 0) { pptpd_log(_this->self, LOG_ERR, "fcntl(F_SET_FL) failed at %s(): %m", __func__); goto fail; } if (bind(sock_gre, (struct sockaddr *)&bind_sin_gre, bind_sin_gre.sin_len) != 0) { pptpd_log(_this->self, LOG_ERR, "bind(%s:%u) failed at %s(): %m", inet_ntoa(bind_sin_gre.sin_addr), ntohs(bind_sin_gre.sin_port), __func__); goto fail; } #ifdef NPPPD_FAKEBIND if (!wildcardbinding) set_faith(sock_gre, 0); #endif if (wildcardbinding) { #ifdef USE_LIBSOCKUTIL if (setsockoptfromto(sock) != 0) { pptpd_log(_this->self, LOG_ERR, "setsockoptfromto() failed in %s(): %m", __func__); goto fail; } #else /* nothing to do */ #endif } pptpd_log(_this->self, LOG_INFO, "Listening %s:gre (PPTP PAC)", inet_ntoa(bind_sin_gre.sin_addr)); _this->sock = sock; _this->sock_gre = sock_gre; event_set(&_this->ev_sock, _this->sock, EV_READ | EV_PERSIST, pptpd_io_event, _this); event_add(&_this->ev_sock, NULL); event_set(&_this->ev_sock_gre, _this->sock_gre, EV_READ | EV_PERSIST, pptpd_gre_io_event, _this); event_add(&_this->ev_sock_gre, NULL); return 0; fail: if (sock >= 0) close(sock); if (sock_gre >= 0) close(sock_gre); _this->sock = -1; _this->sock_gre = -1; return 1; }
/* start l2tpd listner */ static int l2tpd_listener_start(l2tpd_listener *_this) { l2tpd *_l2tpd; int af, lvl, opt, sock, ival; char hbuf[NI_MAXHOST + NI_MAXSERV + 16]; _l2tpd = _this->self; sock = -1; af = _this->bind.sin6.sin6_family; lvl = (af == AF_INET)? IPPROTO_IP : IPPROTO_IPV6; if (_this->tun_name[0] == '\0') strlcpy(_this->tun_name, L2TPD_DEFAULT_LAYER2_LABEL, sizeof(_this->tun_name)); if ((sock = socket(_this->bind.sin6.sin6_family, SOCK_DGRAM | SOCK_NONBLOCK, IPPROTO_UDP)) < 0) { l2tpd_log(_l2tpd, LOG_ERR, "socket() failed in %s(): %m", __func__); goto fail; } #if defined(IP_STRICT_RCVIF) && defined(USE_STRICT_RCVIF) ival = 1; if (setsockopt(sock, IPPROTO_IP, IP_STRICT_RCVIF, &ival, sizeof(ival)) != 0) l2tpd_log(_l2tpd, LOG_WARNING, "%s(): setsockopt(IP_STRICT_RCVIF) failed: %m", __func__); #endif ival = 1; if (setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, &ival, sizeof(ival)) != 0) { l2tpd_log(_l2tpd, LOG_ERR, "setsockopt(,,SO_REUSEPORT) failed in %s(): %m", __func__); goto fail; } if (bind(sock, (struct sockaddr *)&_this->bind, _this->bind.sin6.sin6_len) != 0) { l2tpd_log(_l2tpd, LOG_ERR, "Binding %s/udp: %m", addrport_tostring((struct sockaddr *)&_this->bind, _this->bind.sin6.sin6_len, hbuf, sizeof(hbuf))); goto fail; } #ifdef USE_LIBSOCKUTIL if (setsockoptfromto(sock) != 0) { l2tpd_log(_l2tpd, LOG_ERR, "setsockoptfromto() failed in %s(): %m", __func__); goto fail; } #else opt = (af == AF_INET)? IP_RECVDSTADDR : IPV6_RECVPKTINFO; ival = 1; if (setsockopt(sock, lvl, opt, &ival, sizeof(ival)) != 0) { l2tpd_log(_l2tpd, LOG_ERR, "setsockopt(,,IP{,V6}_RECVDSTADDR) failed in %s(): %m", __func__); goto fail; } #endif #ifdef USE_SA_COOKIE if (af == AF_INET) { ival = 1; if (setsockopt(sock, IPPROTO_IP, IP_IPSECFLOWINFO, &ival, sizeof(ival)) != 0) { l2tpd_log(_l2tpd, LOG_ERR, "setsockopt(,,IP_IPSECFLOWINFO) failed in %s(): %m", __func__); goto fail; } } #endif #ifdef IP_PIPEX opt = (af == AF_INET)? IP_PIPEX : IPV6_PIPEX; ival = 1; if (setsockopt(sock, lvl, opt, &ival, sizeof(ival)) != 0) l2tpd_log(_l2tpd, LOG_WARNING, "%s(): setsockopt(IP{,V6}_PIPEX) failed: %m", __func__); #endif if (_this->conf->require_ipsec) { #ifdef IP_IPSEC_POLICY caddr_t ipsec_policy_in, ipsec_policy_out; opt = (af == AF_INET)? IP_IPSEC_POLICY : IPV6_IPSEC_POLICY; /* * Note: ipsec_set_policy() will assign the buffer for * yacc parser stack, however it never free. * it cause memory leak (-2000byte). */ if ((ipsec_policy_in = ipsec_set_policy(L2TPD_IPSEC_POLICY_IN, strlen(L2TPD_IPSEC_POLICY_IN))) == NULL) { l2tpd_log(_l2tpd, LOG_ERR, "ipsec_set_policy(L2TPD_IPSEC_POLICY_IN) failed " "at %s(): %s: %m", __func__, ipsec_strerror()); } else if (setsockopt(sock, lvl, opt, ipsec_policy_in, ipsec_get_policylen(ipsec_policy_in)) < 0) { l2tpd_log(_l2tpd, LOG_WARNING, "setsockopt(,,IP_IPSEC_POLICY(in)) failed " "in %s(): %m", __func__); } if ((ipsec_policy_out = ipsec_set_policy(L2TPD_IPSEC_POLICY_OUT, strlen(L2TPD_IPSEC_POLICY_OUT))) == NULL) { l2tpd_log(_l2tpd, LOG_ERR, "ipsec_set_policy(L2TPD_IPSEC_POLICY_OUT) failed " "at %s(): %s: %m", __func__, ipsec_strerror()); } if (ipsec_policy_out != NULL && setsockopt(sock, lvl, opt, ipsec_policy_out, ipsec_get_policylen(ipsec_policy_out)) < 0) { l2tpd_log(_l2tpd, LOG_WARNING, "setsockopt(,,IP_IPSEC_POLICY(out)) failed " "in %s(): %m", __func__); } free(ipsec_policy_in); free(ipsec_policy_out); #elif defined(IP_ESP_TRANS_LEVEL) opt = (af == AF_INET) ? IP_ESP_TRANS_LEVEL : IPV6_ESP_TRANS_LEVEL; ival = IPSEC_LEVEL_REQUIRE; if (setsockopt(sock, lvl, opt, &ival, sizeof(ival)) != 0) { l2tpd_log(_l2tpd, LOG_WARNING, "setsockopt(,,IP{,V6}_ESP_TRANS_LEVEL(out)) failed " "in %s(): %m", __func__); } #else #error IP_IPSEC_POLICY or IP_ESP_TRANS_LEVEL must be usable. #endif } _this->sock = sock; event_set(&_this->ev_sock, _this->sock, EV_READ | EV_PERSIST, l2tpd_io_event, _this); event_add(&_this->ev_sock, NULL); l2tpd_log(_l2tpd, LOG_INFO, "Listening %s/udp (L2TP LNS) [%s]", addrport_tostring((struct sockaddr *)&_this->bind, _this->bind.sin6.sin6_len, hbuf, sizeof(hbuf)), _this->tun_name); return 0; fail: if (sock >= 0) close(sock); return 1; }