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