npppd_iface.c revision 1.14
1/* $OpenBSD: npppd_iface.c,v 1.14 2021/01/02 13:15:15 mvs Exp $ */ 2 3/*- 4 * Copyright (c) 2009 Internet Initiative Japan Inc. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28/* $Id: npppd_iface.c,v 1.14 2021/01/02 13:15:15 mvs Exp $ */ 29/**@file 30 * The interface of npppd and kernel. 31 * This is an implementation to use tun(4) or pppx(4). 32 */ 33#include <sys/types.h> 34#include <sys/ioctl.h> 35#include <sys/socket.h> 36#include <sys/uio.h> 37#include <sys/sockio.h> 38#include <netinet/in.h> 39#include <netinet/ip.h> 40#include <arpa/inet.h> 41#include <net/if_dl.h> 42#include <net/if_tun.h> 43#include <net/if_types.h> 44#include <net/if.h> 45#include <net/pipex.h> 46 47#include <fcntl.h> 48 49#include <syslog.h> 50#include <stdio.h> 51#include <stdlib.h> 52#include <string.h> 53#include <unistd.h> 54#include <errno.h> 55#include <stdarg.h> 56 57#include <time.h> 58#include <event.h> 59#include "radish.h" 60 61#include "npppd_defs.h" 62#include "npppd_local.h" 63#include "npppd_subr.h" 64#include "debugutil.h" 65#include "npppd_iface.h" 66 67#ifdef USE_NPPPD_PIPEX 68#include <net/if.h> 69#if defined(__NetBSD__) 70#include <net/if_ether.h> 71#else 72#include <netinet/if_ether.h> 73#endif 74#include <net/pipex.h> 75#endif /* USE_NPPPD_PIPEX */ 76 77#ifdef NPPPD_IFACE_DEBUG 78#define NPPPD_IFACE_DBG(x) npppd_iface_log x 79#define NPPPD_IFACE_ASSERT(cond) \ 80 if (!(cond)) { \ 81 fprintf(stderr, \ 82 "\nASSERT(" #cond ") failed on %s() at %s:%d.\n"\ 83 , __func__, __FILE__, __LINE__); \ 84 abort(); \ 85 } 86#else 87#define NPPPD_IFACE_ASSERT(cond) 88#define NPPPD_IFACE_DBG(x) 89#endif 90 91static void npppd_iface_network_input_ipv4(npppd_iface *, struct pppx_hdr *, 92 u_char *, int); 93static void npppd_iface_network_input(npppd_iface *, u_char *, int); 94static int npppd_iface_setup_ip(npppd_iface *); 95static void npppd_iface_io_event_handler (int, short, void *); 96static int npppd_iface_log (npppd_iface *, int, const char *, ...) 97 __printflike(3,4); 98 99 100/** initialize npppd_iface */ 101void 102npppd_iface_init(npppd *npppd, npppd_iface *_this, struct iface *iface) 103{ 104 105 NPPPD_IFACE_ASSERT(_this != NULL); 106 memset(_this, 0, sizeof(npppd_iface)); 107 108 _this->npppd = npppd; 109 strlcpy(_this->ifname, iface->name, sizeof(_this->ifname)); 110 _this->using_pppx = iface->is_pppx; 111 _this->set_ip4addr = 1; 112 _this->ip4addr = iface->ip4addr; 113 _this->ipcpconf = iface->ipcpconf; 114 _this->devf = -1; 115 _this->initialized = 1; 116} 117 118static int 119npppd_iface_setup_ip(npppd_iface *_this) 120{ 121 int sock, if_flags, changed; 122 struct in_addr gw, assigned; 123 struct sockaddr_in *sin0; 124 struct ifreq ifr; 125 struct ifaliasreq ifra; 126 npppd_ppp *ppp; 127 128 NPPPD_IFACE_ASSERT(_this != NULL); 129 130 sock = -1; 131 changed = 0; 132 memset(&ifr, 0, sizeof(ifr)); 133 134 /* get address which was assigned to interface */ 135 assigned.s_addr = INADDR_NONE; 136 memset(&ifr, 0, sizeof(ifr)); 137 memset(&ifra, 0, sizeof(ifra)); 138 strlcpy(ifr.ifr_name, _this->ifname, sizeof(ifr.ifr_name)); 139 strlcpy(ifra.ifra_name, _this->ifname, sizeof(ifra.ifra_name)); 140 sin0 = (struct sockaddr_in *)&ifr.ifr_addr; 141 142 if (priv_get_if_addr(_this->ifname, &assigned) != 0) { 143 if (errno != EADDRNOTAVAIL) { 144 npppd_iface_log(_this, LOG_ERR, 145 "get ip address failed: %m"); 146 goto fail; 147 } 148 assigned.s_addr = 0; 149 } 150 151 if (assigned.s_addr != _this->ip4addr.s_addr) 152 changed = 1; 153 154 if (priv_get_if_flags(_this->ifname, &if_flags) != 0) { 155 npppd_iface_log(_this, LOG_ERR, 156 "ioctl(,SIOCGIFFLAGS) failed: %m"); 157 goto fail; 158 } 159 if_flags = ifr.ifr_flags; 160 if (_this->set_ip4addr != 0 && changed) { 161 do { 162 struct in_addr dummy; 163 if (priv_delete_if_addr(_this->ifname) != 0) { 164 if (errno == EADDRNOTAVAIL) 165 break; 166 npppd_iface_log(_this, LOG_ERR, 167 "delete ipaddress %s failed: %m", 168 _this->ifname); 169 goto fail; 170 } 171 if (priv_get_if_addr(_this->ifname, &dummy) != 0) { 172 if (errno == EADDRNOTAVAIL) 173 break; 174 npppd_iface_log(_this, LOG_ERR, 175 "cannot get ipaddress %s failed: %m", 176 _this->ifname); 177 goto fail; 178 } 179 } while (1); 180 181 /* ifconfig tun1 down */ 182 if (priv_set_if_flags(_this->ifname, 183 if_flags & ~(IFF_UP | IFF_BROADCAST)) != 0) { 184 npppd_iface_log(_this, LOG_ERR, 185 "disabling %s failed: %m", _this->ifname); 186 goto fail; 187 } 188 if (priv_set_if_addr(_this->ifname, &_this->ip4addr) != 0 && 189 errno != EEXIST) { 190 npppd_iface_log(_this, LOG_ERR, 191 "Cannot assign tun device ip address: %m"); 192 goto fail; 193 } 194 /* erase old route */ 195 if (assigned.s_addr != 0) { 196 gw.s_addr = htonl(INADDR_LOOPBACK); 197 in_host_route_delete(&assigned, &gw); 198 } 199 200 assigned.s_addr = _this->ip4addr.s_addr; 201 202 } 203 _this->ip4addr.s_addr = assigned.s_addr; 204 if (npppd_iface_ip_is_ready(_this)) { 205 if (changed) { 206 /* 207 * If there is a PPP session which was assigned 208 * interface IP address, disconnect it. 209 */ 210 ppp = npppd_get_ppp_by_ip(_this->npppd, _this->ip4addr); 211 if (ppp != NULL) { 212 npppd_iface_log(_this, LOG_ERR, 213 "Assigning %s, but ppp=%d is using " 214 "the address. Requested the ppp to stop", 215 inet_ntoa(_this->ip4addr), ppp->id); 216 ppp_stop(ppp, "Administrative reason"); 217 } 218 } 219 /* ifconfig tun1 up */ 220 if (priv_set_if_flags(_this->ifname, 221 if_flags | IFF_UP | IFF_MULTICAST) != 0) { 222 npppd_iface_log(_this, LOG_ERR, 223 "enabling %s failed: %m", _this->ifname); 224 goto fail; 225 } 226 /* 227 * Add routing entry to communicate from host itself to 228 * _this->ip4addr. 229 */ 230 gw.s_addr = htonl(INADDR_LOOPBACK); 231 in_host_route_add(&_this->ip4addr, &gw, LOOPBACK_IFNAME, 0); 232 } 233 close(sock); sock = -1; 234 235 return 0; 236fail: 237 if (sock >= 0) 238 close(sock); 239 240 return 1; 241} 242 243/** set tunnel end address */ 244int 245npppd_iface_reinit(npppd_iface *_this, struct iface *iface) 246{ 247 int rval; 248 struct in_addr backup; 249 char buf0[128], buf1[128]; 250 251 _this->ipcpconf = iface->ipcpconf; 252 backup = _this->ip4addr; 253 _this->ip4addr = iface->ip4addr; 254 255 if (_this->using_pppx) 256 return 0; 257 if ((rval = npppd_iface_setup_ip(_this)) != 0) 258 return rval; 259 260 if (backup.s_addr != _this->ip4addr.s_addr) { 261 npppd_iface_log(_this, LOG_INFO, "Reinited ip4addr %s=>%s", 262 (backup.s_addr != INADDR_ANY) 263 ? inet_ntop(AF_INET, &backup, buf0, sizeof(buf0)) 264 : "(not assigned)", 265 (_this->ip4addr.s_addr != INADDR_ANY) 266 ? inet_ntop(AF_INET, &_this->ip4addr, buf1, 267 sizeof(buf1)) 268 : "(not assigned)"); 269 } 270 271 return 0; 272} 273 274/** start npppd_iface */ 275int 276npppd_iface_start(npppd_iface *_this) 277{ 278 int x; 279 char buf[PATH_MAX]; 280 281 NPPPD_IFACE_ASSERT(_this != NULL); 282 283 /* open device file */ 284 snprintf(buf, sizeof(buf), "/dev/%s", _this->ifname); 285 if ((_this->devf = priv_open(buf, O_RDWR | O_NONBLOCK)) < 0) { 286 npppd_iface_log(_this, LOG_ERR, "open(%s) failed: %m", buf); 287 goto fail; 288 } 289 290 if (_this->using_pppx == 0) { 291 x = IFF_BROADCAST; 292 if (ioctl(_this->devf, TUNSIFMODE, &x) != 0) { 293 npppd_iface_log(_this, LOG_ERR, 294 "ioctl(TUNSIFMODE=IFF_BROADCAST) failed " 295 "in %s(): %m", __func__); 296 goto fail; 297 } 298 } 299 300 event_set(&_this->ev, _this->devf, EV_READ | EV_PERSIST, 301 npppd_iface_io_event_handler, _this); 302 event_add(&_this->ev, NULL); 303 304 if (_this->using_pppx == 0) { 305 if (npppd_iface_setup_ip(_this) != 0) 306 goto fail; 307 } 308 309#ifndef USE_NPPPD_PIPEX 310 if (_this->using_pppx) { 311 npppd_iface_log(_this, LOG_ERR, 312 "pipex is required when using pppx interface"); 313 goto fail; 314 } 315#endif /* USE_NPPPD_PIPEX */ 316 317 if (_this->using_pppx) { 318 npppd_iface_log(_this, LOG_INFO, "Started pppx"); 319 } else { 320 npppd_iface_log(_this, LOG_INFO, "Started ip4addr=%s", 321 (npppd_iface_ip_is_ready(_this))? 322 inet_ntop(AF_INET, &_this->ip4addr, buf, 323 sizeof(buf)) : "(not assigned)"); 324 } 325 _this->started = 1; 326 327 return 0; 328fail: 329 if (_this->devf >= 0) { 330 event_del(&_this->ev); 331 close(_this->devf); 332 } 333 _this->devf = -1; 334 335 return -1; 336} 337 338/** stop to use npppd_iface */ 339void 340npppd_iface_stop(npppd_iface *_this) 341{ 342 struct in_addr gw; 343 344 NPPPD_IFACE_ASSERT(_this != NULL); 345 if (_this->using_pppx == 0) { 346 priv_delete_if_addr(_this->ifname); 347 gw.s_addr = htonl(INADDR_LOOPBACK); 348 in_host_route_delete(&_this->ip4addr, &gw); 349 } 350 if (_this->devf >= 0) { 351 event_del(&_this->ev); 352 close(_this->devf); 353 npppd_iface_log(_this, LOG_INFO, "Stopped"); 354 } 355 _this->devf = -1; 356 _this->started = 0; 357 event_del(&_this->ev); 358} 359 360/** finalize npppd_iface */ 361void 362npppd_iface_fini(npppd_iface *_this) 363{ 364 NPPPD_IFACE_ASSERT(_this != NULL); 365 _this->initialized = 0; 366} 367 368 369/*********************************************************************** 370 * I/O related functions 371 ***********************************************************************/ 372/** I/O event handler */ 373static void 374npppd_iface_io_event_handler(int fd, short evtype, void *data) 375{ 376 int sz; 377 u_char buffer[8192]; 378 npppd_iface *_this; 379 380 NPPPD_IFACE_ASSERT((evtype & EV_READ) != 0); 381 382 _this = data; 383 NPPPD_IFACE_ASSERT(_this->devf >= 0); 384 do { 385 sz = read(_this->devf, buffer, sizeof(buffer)); 386 if (sz <= 0) { 387 if (sz == 0) 388 npppd_iface_log(_this, LOG_ERR, 389 "file is closed"); 390 else if (errno == EAGAIN) 391 break; 392 else 393 npppd_iface_log(_this, LOG_ERR, 394 "read failed: %m"); 395 npppd_iface_stop(_this); 396 return; 397 } 398 npppd_iface_network_input(_this, buffer, sz); 399 400 } while (1 /* CONSTCOND */); 401 402 return; 403} 404 405/** structure of argument of npppd_iface_network_input_delegate */ 406struct npppd_iface_network_input_arg{ 407 npppd_iface *_this; 408 u_char *pktp; 409 int lpktp; 410}; 411 412/** callback function which works for each PPP session */ 413static int 414npppd_iface_network_input_delegate(struct radish *radish, void *args0) 415{ 416 npppd_ppp *ppp; 417 struct sockaddr_npppd *snp; 418 struct npppd_iface_network_input_arg *args; 419 420 snp = radish->rd_rtent; 421 422 if (snp->snp_type == SNP_PPP) { 423 args = args0; 424 ppp = snp->snp_data_ptr; 425 if (ppp_iface(ppp) != args->_this) 426 return 0; 427#ifdef USE_NPPPD_MPPE 428 if (MPPE_SEND_READY(ppp)) { 429 /* output via MPPE if MPPE started */ 430 mppe_pkt_output(&ppp->mppe, PPP_PROTO_IP, args->pktp, 431 args->lpktp); 432 } else if (MPPE_IS_REQUIRED(ppp)) { 433 /* in case MPPE not started but MPPE is mandatory, */ 434 /* it is not necessary to log because of multicast. */ 435 return 0; 436 } 437#endif 438 ppp_output(ppp, PPP_PROTO_IP, 0, 0, args->pktp, args->lpktp); 439 } 440 441 return 0; 442} 443 444static void 445npppd_iface_network_input_ipv4(npppd_iface *_this, struct pppx_hdr *pppx, 446 u_char *pktp, int lpktp) 447{ 448 struct ip *iphdr; 449 npppd *_npppd; 450 npppd_ppp *ppp; 451 struct npppd_iface_network_input_arg input_arg; 452 453 NPPPD_IFACE_ASSERT(_this != NULL); 454 NPPPD_IFACE_ASSERT(pktp != NULL); 455 456 iphdr = (struct ip *)pktp; 457 _npppd = _this->npppd; 458 459 if (lpktp < sizeof(iphdr)) { 460 npppd_iface_log(_this, LOG_ERR, "Received short packet."); 461 return; 462 } 463 if (_this->using_pppx) 464 ppp = npppd_get_ppp_by_id(_npppd, pppx->pppx_id); 465 else { 466 if (IN_MULTICAST(ntohl(iphdr->ip_dst.s_addr))) { 467 NPPPD_IFACE_ASSERT( 468 ((npppd *)(_this->npppd))->rd != NULL); 469 input_arg._this = _this; 470 input_arg.pktp = pktp; 471 input_arg.lpktp = lpktp; 472 /* delegate */ 473 rd_walktree(((npppd *)(_this->npppd))->rd, 474 npppd_iface_network_input_delegate, &input_arg); 475 return; 476 } 477 ppp = npppd_get_ppp_by_ip(_npppd, iphdr->ip_dst); 478 } 479 480 if (ppp == NULL) { 481#ifdef NPPPD_DEBUG 482 log_printf(LOG_INFO, "%s received a packet to unknown " 483 "%s.", _this->ifname, inet_ntoa(iphdr->ip_dst)); 484#endif 485 return; 486 } 487#ifndef NO_ADJUST_MSS 488 if (ppp->adjust_mss) { 489 adjust_tcp_mss(pktp, lpktp, MRU_IPMTU(ppp->peer_mru)); 490 } 491#endif 492 if (ppp->timeout_sec > 0 && !ip_is_idle_packet(iphdr, lpktp)) 493 ppp_reset_idle_timeout(ppp); 494 495#ifdef USE_NPPPD_MPPE 496 if (MPPE_SEND_READY(ppp)) { 497 /* output via MPPE if MPPE started */ 498 mppe_pkt_output(&ppp->mppe, PPP_PROTO_IP, pktp, lpktp); 499 return; 500 } else if (MPPE_IS_REQUIRED(ppp)) { 501 /* in case MPPE not started but MPPE is mandatory */ 502 ppp_log(ppp, LOG_WARNING, "A packet received from network, " 503 "but MPPE is not started."); 504 return; 505 } 506#endif 507 ppp_output(ppp, PPP_PROTO_IP, 0, 0, pktp, lpktp); 508} 509 510/** 511 * This function is called when an input packet come from network(tun). 512 * Currently, it assumes that it input IPv4 packet. 513 */ 514static void 515npppd_iface_network_input(npppd_iface *_this, u_char *pktp, int lpktp) 516{ 517 uint32_t af; 518 struct pppx_hdr *pppx = NULL; 519 520 if (_this->using_pppx) { 521 if (lpktp < sizeof(struct pppx_hdr)) { 522 npppd_iface_log(_this, LOG_ERR, 523 "Received short packet."); 524 return; 525 } 526 pppx = (struct pppx_hdr *)pktp; 527 pktp += sizeof(struct pppx_hdr); 528 lpktp -= sizeof(struct pppx_hdr); 529 } 530 531 if (lpktp < sizeof(uint32_t)) { 532 npppd_iface_log(_this, LOG_ERR, "Received short packet."); 533 return; 534 } 535 GETLONG(af, pktp); 536 lpktp -= sizeof(uint32_t); 537 538 switch (af) { 539 case AF_INET: 540 npppd_iface_network_input_ipv4(_this, pppx, pktp, lpktp); 541 break; 542 543 default: 544 NPPPD_IFACE_ASSERT(0); 545 break; 546 547 } 548} 549 550/** write to tunnel device */ 551void 552npppd_iface_write(npppd_iface *_this, npppd_ppp *ppp, int proto, u_char *pktp, 553 int lpktp) 554{ 555 int niov = 0, tlen; 556 uint32_t th; 557 struct iovec iov[3]; 558 struct pppx_hdr pppx; 559 NPPPD_IFACE_ASSERT(_this != NULL); 560 NPPPD_IFACE_ASSERT(_this->devf >= 0); 561 562 tlen = 0; 563 th = htonl(proto); 564 if (_this->using_pppx) { 565 pppx.pppx_proto = npppd_pipex_proto(ppp->tunnel_type); 566 pppx.pppx_id = ppp->tunnel_session_id; 567 iov[niov].iov_base = &pppx; 568 iov[niov++].iov_len = sizeof(pppx); 569 tlen += sizeof(pppx); 570 } 571 iov[niov].iov_base = &th; 572 iov[niov++].iov_len = sizeof(th); 573 tlen += sizeof(th); 574 iov[niov].iov_base = pktp; 575 iov[niov++].iov_len = lpktp; 576 tlen += lpktp; 577 578 if (writev(_this->devf, iov, niov) != tlen) 579 npppd_iface_log(_this, LOG_ERR, "write failed: %m"); 580} 581 582/*********************************************************************** 583 * misc functions 584 ***********************************************************************/ 585/** Log it which starts the label based on this instance. */ 586static int 587npppd_iface_log(npppd_iface *_this, int prio, const char *fmt, ...) 588{ 589 int status; 590 char logbuf[BUFSIZ]; 591 va_list ap; 592 593 NPPPD_IFACE_ASSERT(_this != NULL); 594 595 va_start(ap, fmt); 596 snprintf(logbuf, sizeof(logbuf), "%s %s", _this->ifname, fmt); 597 status = vlog_printf(prio, logbuf, ap); 598 va_end(ap); 599 600 return status; 601} 602