1 /*- 2 * Copyright (c) 2001 Charles Mott <cm@linktel.net> 3 * Brian Somers <brian@Awfulhak.org> 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 * 27 * $FreeBSD: src/usr.sbin/ppp/nat_cmd.c,v 1.35.2.13 2002/09/01 02:12:29 brian Exp $ 28 * $DragonFly: src/usr.sbin/ppp/nat_cmd.c,v 1.2 2003/06/17 04:30:00 dillon Exp $ 29 */ 30 31 #include <sys/param.h> 32 #include <netinet/in.h> 33 #include <arpa/inet.h> 34 #include <netdb.h> 35 #include <netinet/in_systm.h> 36 #include <netinet/in.h> 37 #include <netinet/ip.h> 38 #include <sys/socket.h> 39 #include <sys/un.h> 40 41 #include <stdarg.h> 42 #include <stdio.h> 43 #include <stdlib.h> 44 #include <string.h> 45 #include <termios.h> 46 47 #ifdef LOCALNAT 48 #include "alias.h" 49 #else 50 #include <alias.h> 51 #endif 52 53 #include "layer.h" 54 #include "proto.h" 55 #include "defs.h" 56 #include "command.h" 57 #include "log.h" 58 #include "nat_cmd.h" 59 #include "descriptor.h" 60 #include "prompt.h" 61 #include "timer.h" 62 #include "fsm.h" 63 #include "slcompress.h" 64 #include "throughput.h" 65 #include "iplist.h" 66 #include "mbuf.h" 67 #include "lqr.h" 68 #include "hdlc.h" 69 #include "ncpaddr.h" 70 #include "ip.h" 71 #include "ipcp.h" 72 #include "ipv6cp.h" 73 #include "lcp.h" 74 #include "ccp.h" 75 #include "link.h" 76 #include "mp.h" 77 #include "filter.h" 78 #ifndef NORADIUS 79 #include "radius.h" 80 #endif 81 #include "ncp.h" 82 #include "bundle.h" 83 84 85 #define NAT_EXTRABUF (13) 86 87 static int StrToAddr(const char *, struct in_addr *); 88 static int StrToPortRange(const char *, u_short *, u_short *, const char *); 89 static int StrToAddrAndPort(const char *, struct in_addr *, u_short *, 90 u_short *, const char *); 91 92 static void 93 lowhigh(u_short *a, u_short *b) 94 { 95 if (a > b) { 96 u_short c; 97 98 c = *b; 99 *b = *a; 100 *a = c; 101 } 102 } 103 104 int 105 nat_RedirectPort(struct cmdargs const *arg) 106 { 107 if (!arg->bundle->NatEnabled) { 108 prompt_Printf(arg->prompt, "Alias not enabled\n"); 109 return 1; 110 } else if (arg->argc == arg->argn + 3 || arg->argc == arg->argn + 4) { 111 char proto_constant; 112 const char *proto; 113 struct in_addr localaddr; 114 u_short hlocalport, llocalport; 115 struct in_addr aliasaddr; 116 u_short haliasport, laliasport; 117 struct in_addr remoteaddr; 118 u_short hremoteport, lremoteport; 119 struct alias_link *link; 120 int error; 121 122 proto = arg->argv[arg->argn]; 123 if (strcmp(proto, "tcp") == 0) { 124 proto_constant = IPPROTO_TCP; 125 } else if (strcmp(proto, "udp") == 0) { 126 proto_constant = IPPROTO_UDP; 127 } else { 128 prompt_Printf(arg->prompt, "port redirect: protocol must be" 129 " tcp or udp\n"); 130 return -1; 131 } 132 133 error = StrToAddrAndPort(arg->argv[arg->argn+1], &localaddr, &llocalport, 134 &hlocalport, proto); 135 if (error) { 136 prompt_Printf(arg->prompt, "nat port: error reading localaddr:port\n"); 137 return -1; 138 } 139 140 error = StrToPortRange(arg->argv[arg->argn+2], &laliasport, &haliasport, 141 proto); 142 if (error) { 143 prompt_Printf(arg->prompt, "nat port: error reading alias port\n"); 144 return -1; 145 } 146 aliasaddr.s_addr = INADDR_ANY; 147 148 if (arg->argc == arg->argn + 4) { 149 error = StrToAddrAndPort(arg->argv[arg->argn+3], &remoteaddr, 150 &lremoteport, &hremoteport, proto); 151 if (error) { 152 prompt_Printf(arg->prompt, "nat port: error reading " 153 "remoteaddr:port\n"); 154 return -1; 155 } 156 } else { 157 remoteaddr.s_addr = INADDR_ANY; 158 lremoteport = hremoteport = 0; 159 } 160 161 lowhigh(&llocalport, &hlocalport); 162 lowhigh(&laliasport, &haliasport); 163 lowhigh(&lremoteport, &hremoteport); 164 165 if (haliasport - laliasport != hlocalport - llocalport) { 166 prompt_Printf(arg->prompt, "nat port: local & alias port ranges " 167 "are not equal\n"); 168 return -1; 169 } 170 171 if (hremoteport && hremoteport - lremoteport != hlocalport - llocalport) { 172 prompt_Printf(arg->prompt, "nat port: local & remote port ranges " 173 "are not equal\n"); 174 return -1; 175 } 176 177 while (laliasport <= haliasport) { 178 link = PacketAliasRedirectPort(localaddr, htons(llocalport), 179 remoteaddr, htons(lremoteport), 180 aliasaddr, htons(laliasport), 181 proto_constant); 182 183 if (link == NULL) { 184 prompt_Printf(arg->prompt, "nat port: %d: error %d\n", laliasport, 185 error); 186 return 1; 187 } 188 llocalport++; 189 laliasport++; 190 if (hremoteport) 191 lremoteport++; 192 } 193 194 return 0; 195 } 196 197 return -1; 198 } 199 200 201 int 202 nat_RedirectAddr(struct cmdargs const *arg) 203 { 204 if (!arg->bundle->NatEnabled) { 205 prompt_Printf(arg->prompt, "nat not enabled\n"); 206 return 1; 207 } else if (arg->argc == arg->argn+2) { 208 int error; 209 struct in_addr localaddr, aliasaddr; 210 struct alias_link *link; 211 212 error = StrToAddr(arg->argv[arg->argn], &localaddr); 213 if (error) { 214 prompt_Printf(arg->prompt, "address redirect: invalid local address\n"); 215 return 1; 216 } 217 error = StrToAddr(arg->argv[arg->argn+1], &aliasaddr); 218 if (error) { 219 prompt_Printf(arg->prompt, "address redirect: invalid alias address\n"); 220 prompt_Printf(arg->prompt, "usage: nat %s %s\n", arg->cmd->name, 221 arg->cmd->syntax); 222 return 1; 223 } 224 link = PacketAliasRedirectAddr(localaddr, aliasaddr); 225 if (link == NULL) { 226 prompt_Printf(arg->prompt, "address redirect: packet aliasing" 227 " engine error\n"); 228 prompt_Printf(arg->prompt, "usage: nat %s %s\n", arg->cmd->name, 229 arg->cmd->syntax); 230 } 231 } else 232 return -1; 233 234 return 0; 235 } 236 237 238 int 239 nat_RedirectProto(struct cmdargs const *arg) 240 { 241 if (!arg->bundle->NatEnabled) { 242 prompt_Printf(arg->prompt, "nat not enabled\n"); 243 return 1; 244 } else if (arg->argc >= arg->argn + 2 && arg->argc <= arg->argn + 4) { 245 struct in_addr localIP, publicIP, remoteIP; 246 struct alias_link *link; 247 struct protoent *pe; 248 int error, len; 249 250 len = strlen(arg->argv[arg->argn]); 251 if (len == 0) { 252 prompt_Printf(arg->prompt, "proto redirect: invalid protocol\n"); 253 return 1; 254 } 255 if (strspn(arg->argv[arg->argn], "01234567") == len) 256 pe = getprotobynumber(atoi(arg->argv[arg->argn])); 257 else 258 pe = getprotobyname(arg->argv[arg->argn]); 259 if (pe == NULL) { 260 prompt_Printf(arg->prompt, "proto redirect: invalid protocol\n"); 261 return 1; 262 } 263 264 error = StrToAddr(arg->argv[arg->argn + 1], &localIP); 265 if (error) { 266 prompt_Printf(arg->prompt, "proto redirect: invalid src address\n"); 267 return 1; 268 } 269 270 if (arg->argc >= arg->argn + 3) { 271 error = StrToAddr(arg->argv[arg->argn + 2], &publicIP); 272 if (error) { 273 prompt_Printf(arg->prompt, "proto redirect: invalid alias address\n"); 274 prompt_Printf(arg->prompt, "usage: nat %s %s\n", arg->cmd->name, 275 arg->cmd->syntax); 276 return 1; 277 } 278 } else 279 publicIP.s_addr = INADDR_ANY; 280 281 if (arg->argc == arg->argn + 4) { 282 error = StrToAddr(arg->argv[arg->argn + 2], &remoteIP); 283 if (error) { 284 prompt_Printf(arg->prompt, "proto redirect: invalid dst address\n"); 285 prompt_Printf(arg->prompt, "usage: nat %s %s\n", arg->cmd->name, 286 arg->cmd->syntax); 287 return 1; 288 } 289 } else 290 remoteIP.s_addr = INADDR_ANY; 291 292 link = PacketAliasRedirectProto(localIP, remoteIP, publicIP, pe->p_proto); 293 if (link == NULL) { 294 prompt_Printf(arg->prompt, "proto redirect: packet aliasing" 295 " engine error\n"); 296 prompt_Printf(arg->prompt, "usage: nat %s %s\n", arg->cmd->name, 297 arg->cmd->syntax); 298 } 299 } else 300 return -1; 301 302 return 0; 303 } 304 305 306 static int 307 StrToAddr(const char *str, struct in_addr *addr) 308 { 309 struct hostent *hp; 310 311 if (inet_aton(str, addr)) 312 return 0; 313 314 hp = gethostbyname(str); 315 if (!hp) { 316 log_Printf(LogWARN, "StrToAddr: Unknown host %s.\n", str); 317 return -1; 318 } 319 *addr = *((struct in_addr *) hp->h_addr); 320 return 0; 321 } 322 323 324 static int 325 StrToPort(const char *str, u_short *port, const char *proto) 326 { 327 struct servent *sp; 328 char *end; 329 330 *port = strtol(str, &end, 10); 331 if (*end != '\0') { 332 sp = getservbyname(str, proto); 333 if (sp == NULL) { 334 log_Printf(LogWARN, "StrToAddr: Unknown port or service %s/%s.\n", 335 str, proto); 336 return -1; 337 } 338 *port = ntohs(sp->s_port); 339 } 340 341 return 0; 342 } 343 344 static int 345 StrToPortRange(const char *str, u_short *low, u_short *high, const char *proto) 346 { 347 char *minus; 348 int res; 349 350 minus = strchr(str, '-'); 351 if (minus) 352 *minus = '\0'; /* Cheat the const-ness ! */ 353 354 res = StrToPort(str, low, proto); 355 356 if (minus) 357 *minus = '-'; /* Cheat the const-ness ! */ 358 359 if (res == 0) { 360 if (minus) 361 res = StrToPort(minus + 1, high, proto); 362 else 363 *high = *low; 364 } 365 366 return res; 367 } 368 369 static int 370 StrToAddrAndPort(const char *str, struct in_addr *addr, u_short *low, 371 u_short *high, const char *proto) 372 { 373 char *colon; 374 int res; 375 376 colon = strchr(str, ':'); 377 if (!colon) { 378 log_Printf(LogWARN, "StrToAddrAndPort: %s is missing port number.\n", str); 379 return -1; 380 } 381 382 *colon = '\0'; /* Cheat the const-ness ! */ 383 res = StrToAddr(str, addr); 384 *colon = ':'; /* Cheat the const-ness ! */ 385 if (res != 0) 386 return -1; 387 388 return StrToPortRange(colon + 1, low, high, proto); 389 } 390 391 int 392 nat_ProxyRule(struct cmdargs const *arg) 393 { 394 char cmd[LINE_LEN]; 395 int f, pos; 396 size_t len; 397 398 if (arg->argn >= arg->argc) 399 return -1; 400 401 for (f = arg->argn, pos = 0; f < arg->argc; f++) { 402 len = strlen(arg->argv[f]); 403 if (sizeof cmd - pos < len + (len ? 1 : 0)) 404 break; 405 if (len) 406 cmd[pos++] = ' '; 407 strcpy(cmd + pos, arg->argv[f]); 408 pos += len; 409 } 410 411 return PacketAliasProxyRule(cmd); 412 } 413 414 int 415 nat_SetTarget(struct cmdargs const *arg) 416 { 417 struct in_addr addr; 418 419 if (arg->argc == arg->argn) { 420 addr.s_addr = INADDR_ANY; 421 PacketAliasSetTarget(addr); 422 return 0; 423 } 424 425 if (arg->argc != arg->argn + 1) 426 return -1; 427 428 if (!strcasecmp(arg->argv[arg->argn], "MYADDR")) { 429 addr.s_addr = INADDR_ANY; 430 PacketAliasSetTarget(addr); 431 return 0; 432 } 433 434 addr = GetIpAddr(arg->argv[arg->argn]); 435 if (addr.s_addr == INADDR_NONE) { 436 log_Printf(LogWARN, "%s: invalid address\n", arg->argv[arg->argn]); 437 return 1; 438 } 439 440 PacketAliasSetTarget(addr); 441 return 0; 442 } 443 444 #ifndef NO_FW_PUNCH 445 int 446 nat_PunchFW(struct cmdargs const *arg) 447 { 448 char *end; 449 long base, count; 450 451 if (arg->argc == arg->argn) { 452 PacketAliasSetMode(0, PKT_ALIAS_PUNCH_FW); 453 return 0; 454 } 455 456 if (arg->argc != arg->argn + 2) 457 return -1; 458 459 base = strtol(arg->argv[arg->argn], &end, 10); 460 if (*end != '\0' || base < 0) 461 return -1; 462 463 count = strtol(arg->argv[arg->argn + 1], &end, 10); 464 if (*end != '\0' || count < 0) 465 return -1; 466 467 PacketAliasSetFWBase(base, count); 468 PacketAliasSetMode(PKT_ALIAS_PUNCH_FW, PKT_ALIAS_PUNCH_FW); 469 470 return 0; 471 } 472 #endif 473 474 static struct mbuf * 475 nat_LayerPush(struct bundle *bundle, struct link *l, struct mbuf *bp, 476 int pri, u_short *proto) 477 { 478 if (!bundle->NatEnabled || *proto != PROTO_IP) 479 return bp; 480 481 log_Printf(LogDEBUG, "nat_LayerPush: PROTO_IP -> PROTO_IP\n"); 482 m_settype(bp, MB_NATOUT); 483 /* Ensure there's a bit of extra buffer for the NAT code... */ 484 bp = m_pullup(m_append(bp, NULL, NAT_EXTRABUF)); 485 PacketAliasOut(MBUF_CTOP(bp), bp->m_len); 486 bp->m_len = ntohs(((struct ip *)MBUF_CTOP(bp))->ip_len); 487 488 return bp; 489 } 490 491 static struct mbuf * 492 nat_LayerPull(struct bundle *bundle, struct link *l, struct mbuf *bp, 493 u_short *proto) 494 { 495 static int gfrags; 496 int ret, len, nfrags; 497 struct mbuf **last; 498 char *fptr; 499 500 if (!bundle->NatEnabled || *proto != PROTO_IP) 501 return bp; 502 503 log_Printf(LogDEBUG, "nat_LayerPull: PROTO_IP -> PROTO_IP\n"); 504 m_settype(bp, MB_NATIN); 505 /* Ensure there's a bit of extra buffer for the NAT code... */ 506 bp = m_pullup(m_append(bp, NULL, NAT_EXTRABUF)); 507 ret = PacketAliasIn(MBUF_CTOP(bp), bp->m_len); 508 509 bp->m_len = ntohs(((struct ip *)MBUF_CTOP(bp))->ip_len); 510 if (bp->m_len > MAX_MRU) { 511 log_Printf(LogWARN, "nat_LayerPull: Problem with IP header length (%lu)\n", 512 (unsigned long)bp->m_len); 513 m_freem(bp); 514 return NULL; 515 } 516 517 switch (ret) { 518 case PKT_ALIAS_OK: 519 break; 520 521 case PKT_ALIAS_UNRESOLVED_FRAGMENT: 522 /* Save the data for later */ 523 fptr = malloc(bp->m_len); 524 bp = mbuf_Read(bp, fptr, bp->m_len); 525 PacketAliasSaveFragment(fptr); 526 log_Printf(LogDEBUG, "Store another frag (%lu) - now %d\n", 527 (unsigned long)((struct ip *)fptr)->ip_id, ++gfrags); 528 break; 529 530 case PKT_ALIAS_FOUND_HEADER_FRAGMENT: 531 /* Fetch all the saved fragments and chain them on the end of `bp' */ 532 last = &bp->m_nextpkt; 533 nfrags = 0; 534 while ((fptr = PacketAliasGetFragment(MBUF_CTOP(bp))) != NULL) { 535 nfrags++; 536 PacketAliasFragmentIn(MBUF_CTOP(bp), fptr); 537 len = ntohs(((struct ip *)fptr)->ip_len); 538 *last = m_get(len, MB_NATIN); 539 memcpy(MBUF_CTOP(*last), fptr, len); 540 free(fptr); 541 last = &(*last)->m_nextpkt; 542 } 543 gfrags -= nfrags; 544 log_Printf(LogDEBUG, "Found a frag header (%lu) - plus %d more frags (no" 545 "w %d)\n", (unsigned long)((struct ip *)MBUF_CTOP(bp))->ip_id, 546 nfrags, gfrags); 547 break; 548 549 case PKT_ALIAS_IGNORED: 550 if (PacketAliasSetMode(0, 0) & PKT_ALIAS_DENY_INCOMING) { 551 log_Printf(LogTCPIP, "NAT engine denied data:\n"); 552 m_freem(bp); 553 bp = NULL; 554 } else if (log_IsKept(LogTCPIP)) { 555 log_Printf(LogTCPIP, "NAT engine ignored data:\n"); 556 PacketCheck(bundle, AF_INET, MBUF_CTOP(bp), bp->m_len, NULL, 557 NULL, NULL); 558 } 559 break; 560 561 default: 562 log_Printf(LogWARN, "nat_LayerPull: Dropped a packet (%d)....\n", ret); 563 m_freem(bp); 564 bp = NULL; 565 break; 566 } 567 568 return bp; 569 } 570 571 struct layer natlayer = 572 { LAYER_NAT, "nat", nat_LayerPush, nat_LayerPull }; 573