nat_cmd.c revision 58867
18876Srgrimes/*-
24Srgrimes * The code in this file was written by Eivind Eklund <perhaps@yes.no>,
34Srgrimes * who places it in the public domain without restriction.
44Srgrimes *
58876Srgrimes * $FreeBSD: head/usr.sbin/ppp/nat_cmd.c 58867 2000-03-31 14:26:23Z brian $
64Srgrimes */
74Srgrimes
84Srgrimes#include <sys/param.h>
94Srgrimes#include <netinet/in.h>
104Srgrimes#include <arpa/inet.h>
118876Srgrimes#include <netdb.h>
128876Srgrimes#include <netinet/in_systm.h>
134Srgrimes#include <netinet/in.h>
144Srgrimes#include <netinet/ip.h>
158876Srgrimes#include <sys/un.h>
164Srgrimes
178876Srgrimes#include <stdio.h>
184Srgrimes#include <stdlib.h>
194Srgrimes#include <string.h>
204Srgrimes#include <termios.h>
214Srgrimes
228876Srgrimes#ifdef LOCALNAT
234Srgrimes#include "alias.h"
244Srgrimes#else
254Srgrimes#include <alias.h>
2612720Sphk#endif
274Srgrimes
28623Srgrimes#include "layer.h"
294Srgrimes#include "proto.h"
304Srgrimes#include "defs.h"
314Srgrimes#include "command.h"
324Srgrimes#include "log.h"
334Srgrimes#include "nat_cmd.h"
342056Swollman#include "descriptor.h"
352056Swollman#include "prompt.h"
362056Swollman#include "timer.h"
3712662Sdg#include "fsm.h"
3812662Sdg#include "slcompress.h"
3912662Sdg#include "throughput.h"
4012662Sdg#include "iplist.h"
4112662Sdg#include "mbuf.h"
4212662Sdg#include "lqr.h"
432056Swollman#include "hdlc.h"
444Srgrimes#include "ipcp.h"
454Srgrimes#include "lcp.h"
464Srgrimes#include "ccp.h"
474Srgrimes#include "link.h"
484Srgrimes#include "mp.h"
494Srgrimes#include "filter.h"
504Srgrimes#ifndef NORADIUS
514Srgrimes#include "radius.h"
524Srgrimes#endif
534Srgrimes#include "ip.h"
5412515Sphk#include "bundle.h"
554Srgrimes
564Srgrimes
5712720Sphk#define NAT_EXTRABUF (13)
5812515Sphk
5912515Sphkstatic int StrToAddr(const char *, struct in_addr *);
6012515Sphkstatic int StrToPortRange(const char *, u_short *, u_short *, const char *);
614Srgrimesstatic int StrToAddrAndPort(const char *, struct in_addr *, u_short *,
6212515Sphk                            u_short *, const char *);
6312515Sphk
6412515Sphkstatic void
6512515Sphklowhigh(u_short *a, u_short *b)
6612515Sphk{
6712515Sphk  if (a > b) {
6812515Sphk    u_short c;
6912515Sphk
7012515Sphk    c = *b;
7112515Sphk    *b = *a;
7212515Sphk    *a = c;
7312473Sbde  }
7412515Sphk}
754Srgrimes
764Srgrimesint
774Srgrimesnat_RedirectPort(struct cmdargs const *arg)
784Srgrimes{
794Srgrimes  if (!arg->bundle->NatEnabled) {
804Srgrimes    prompt_Printf(arg->prompt, "Alias not enabled\n");
814Srgrimes    return 1;
824Srgrimes  } else if (arg->argc == arg->argn + 3 || arg->argc == arg->argn + 4) {
834Srgrimes    char proto_constant;
844Srgrimes    const char *proto;
854Srgrimes    struct in_addr localaddr;
864Srgrimes    u_short hlocalport, llocalport;
874Srgrimes    struct in_addr aliasaddr;
884Srgrimes    u_short haliasport, laliasport;
894Srgrimes    struct in_addr remoteaddr;
904Srgrimes    u_short hremoteport, lremoteport;
914Srgrimes    struct alias_link *link;
924Srgrimes    int error;
934Srgrimes
944Srgrimes    proto = arg->argv[arg->argn];
954Srgrimes    if (strcmp(proto, "tcp") == 0) {
964Srgrimes      proto_constant = IPPROTO_TCP;
974Srgrimes    } else if (strcmp(proto, "udp") == 0) {
984Srgrimes      proto_constant = IPPROTO_UDP;
994Srgrimes    } else {
1004Srgrimes      prompt_Printf(arg->prompt, "port redirect: protocol must be"
1014Srgrimes                    " tcp or udp\n");
10212515Sphk      return -1;
1034Srgrimes    }
1044Srgrimes
1054Srgrimes    error = StrToAddrAndPort(arg->argv[arg->argn+1], &localaddr, &llocalport,
1064Srgrimes                             &hlocalport, proto);
1074Srgrimes    if (error) {
1084Srgrimes      prompt_Printf(arg->prompt, "nat port: error reading localaddr:port\n");
1094Srgrimes      return -1;
1104Srgrimes    }
1114Srgrimes
1124Srgrimes    error = StrToPortRange(arg->argv[arg->argn+2], &laliasport, &haliasport,
1134Srgrimes                           proto);
1144Srgrimes    if (error) {
1154Srgrimes      prompt_Printf(arg->prompt, "nat port: error reading alias port\n");
1164Srgrimes      return -1;
1174Srgrimes    }
1184Srgrimes    aliasaddr.s_addr = INADDR_ANY;
1194Srgrimes
1204Srgrimes    if (arg->argc == arg->argn + 4) {
1214Srgrimes      error = StrToAddrAndPort(arg->argv[arg->argn+3], &remoteaddr,
1224Srgrimes                               &lremoteport, &hremoteport, proto);
1234Srgrimes      if (error) {
1244Srgrimes        prompt_Printf(arg->prompt, "nat port: error reading "
1254Srgrimes                      "remoteaddr:port\n");
1264Srgrimes        return -1;
1274Srgrimes      }
1284Srgrimes    } else {
1294Srgrimes      remoteaddr.s_addr = INADDR_ANY;
1304Srgrimes      lremoteport = hremoteport = 0;
1314Srgrimes    }
1324Srgrimes
1334Srgrimes    lowhigh(&llocalport, &hlocalport);
1344Srgrimes    lowhigh(&laliasport, &haliasport);
1354Srgrimes    lowhigh(&lremoteport, &hremoteport);
1364Srgrimes
1374Srgrimes    if (haliasport - laliasport != hlocalport - llocalport) {
1384Srgrimes      prompt_Printf(arg->prompt, "nat port: local & alias port ranges "
1394Srgrimes                    "are not equal\n");
1404Srgrimes      return -1;
1414Srgrimes    }
1424Srgrimes
1434Srgrimes    if (hremoteport && hremoteport - lremoteport != hlocalport - llocalport) {
1444Srgrimes      prompt_Printf(arg->prompt, "nat port: local & remote port ranges "
14512515Sphk                    "are not equal\n");
1464Srgrimes      return -1;
1474Srgrimes    }
1484Srgrimes
1494Srgrimes    while (laliasport <= haliasport) {
1504Srgrimes      link = PacketAliasRedirectPort(localaddr, htons(llocalport),
1514Srgrimes				     remoteaddr, htons(lremoteport),
1524Srgrimes                                     aliasaddr, htons(laliasport),
1534Srgrimes				     proto_constant);
1544Srgrimes
1554Srgrimes      if (link == NULL) {
1564Srgrimes        prompt_Printf(arg->prompt, "nat port: %d: error %d\n", laliasport,
1574Srgrimes                      error);
1584Srgrimes        return 1;
1594Srgrimes      }
1604Srgrimes      llocalport++;
1614Srgrimes      laliasport++;
1624Srgrimes      if (hremoteport)
1634Srgrimes        lremoteport++;
1644Srgrimes    }
1654Srgrimes
1664Srgrimes    return 0;
16712515Sphk  }
1684Srgrimes
1694Srgrimes  return -1;
1704Srgrimes}
1714Srgrimes
1724Srgrimes
1734Srgrimesint
1744Srgrimesnat_RedirectAddr(struct cmdargs const *arg)
1754Srgrimes{
1764Srgrimes  if (!arg->bundle->NatEnabled) {
1774Srgrimes    prompt_Printf(arg->prompt, "nat not enabled\n");
1784Srgrimes    return 1;
1794Srgrimes  } else if (arg->argc == arg->argn+2) {
1804Srgrimes    int error;
1814Srgrimes    struct in_addr localaddr, aliasaddr;
1824Srgrimes    struct alias_link *link;
1834Srgrimes
1844Srgrimes    error = StrToAddr(arg->argv[arg->argn], &localaddr);
1854Srgrimes    if (error) {
1864Srgrimes      prompt_Printf(arg->prompt, "address redirect: invalid local address\n");
1874Srgrimes      return 1;
1884Srgrimes    }
1894Srgrimes    error = StrToAddr(arg->argv[arg->argn+1], &aliasaddr);
1904Srgrimes    if (error) {
1914Srgrimes      prompt_Printf(arg->prompt, "address redirect: invalid alias address\n");
19212473Sbde      prompt_Printf(arg->prompt, "Usage: nat %s %s\n", arg->cmd->name,
1934Srgrimes                    arg->cmd->syntax);
1944Srgrimes      return 1;
1954Srgrimes    }
1964Srgrimes    link = PacketAliasRedirectAddr(localaddr, aliasaddr);
1974Srgrimes    if (link == NULL) {
1984Srgrimes      prompt_Printf(arg->prompt, "address redirect: packet aliasing"
1994Srgrimes                    " engine error\n");
2004Srgrimes      prompt_Printf(arg->prompt, "Usage: nat %s %s\n", arg->cmd->name,
2014Srgrimes                    arg->cmd->syntax);
2024Srgrimes    }
2034Srgrimes  } else
20412473Sbde    return -1;
2054Srgrimes
2064Srgrimes  return 0;
2074Srgrimes}
2084Srgrimes
2094Srgrimes
2104Srgrimesstatic int
2114SrgrimesStrToAddr(const char *str, struct in_addr *addr)
2124Srgrimes{
2134Srgrimes  struct hostent *hp;
2144Srgrimes
2154Srgrimes  if (inet_aton(str, addr))
2164Srgrimes    return 0;
2174Srgrimes
2184Srgrimes  hp = gethostbyname(str);
2194Srgrimes  if (!hp) {
2204Srgrimes    log_Printf(LogWARN, "StrToAddr: Unknown host %s.\n", str);
2214Srgrimes    return -1;
22212473Sbde  }
22312473Sbde  *addr = *((struct in_addr *) hp->h_addr);
22412473Sbde  return 0;
22512473Sbde}
22612473Sbde
2274Srgrimes
2284Srgrimesstatic int
2294SrgrimesStrToPort(const char *str, u_short *port, const char *proto)
2304Srgrimes{
2314Srgrimes  struct servent *sp;
2324Srgrimes  char *end;
2334Srgrimes
2344Srgrimes  *port = strtol(str, &end, 10);
2354Srgrimes  if (*end != '\0') {
2364Srgrimes    sp = getservbyname(str, proto);
2374Srgrimes    if (sp == NULL) {
2384Srgrimes      log_Printf(LogWARN, "StrToAddr: Unknown port or service %s/%s.\n",
2394Srgrimes	        str, proto);
2404Srgrimes      return -1;
2414Srgrimes    }
2424Srgrimes    *port = ntohs(sp->s_port);
2434Srgrimes  }
2444Srgrimes
2454Srgrimes  return 0;
2464Srgrimes}
2474Srgrimes
2484Srgrimesstatic int
2494SrgrimesStrToPortRange(const char *str, u_short *low, u_short *high, const char *proto)
2504Srgrimes{
2514Srgrimes  char *minus;
2524Srgrimes  int res;
2534Srgrimes
2544Srgrimes  minus = strchr(str, '-');
25512515Sphk  if (minus)
25612515Sphk    *minus = '\0';		/* Cheat the const-ness ! */
2574Srgrimes
2584Srgrimes  res = StrToPort(str, low, proto);
2594Srgrimes
2604Srgrimes  if (minus)
2614Srgrimes    *minus = '-';		/* Cheat the const-ness ! */
2624Srgrimes
2634Srgrimes  if (res == 0) {
2644Srgrimes    if (minus)
2654Srgrimes      res = StrToPort(minus + 1, high, proto);
2664Srgrimes    else
2674Srgrimes      *high = *low;
2684Srgrimes  }
2694Srgrimes
2704Srgrimes  return res;
2714Srgrimes}
2724Srgrimes
2734Srgrimesstatic int
2744SrgrimesStrToAddrAndPort(const char *str, struct in_addr *addr, u_short *low,
2754Srgrimes                 u_short *high, const char *proto)
2764Srgrimes{
2774Srgrimes  char *colon;
2784Srgrimes  int res;
2794Srgrimes
2804Srgrimes  colon = strchr(str, ':');
2814Srgrimes  if (!colon) {
2824Srgrimes    log_Printf(LogWARN, "StrToAddrAndPort: %s is missing port number.\n", str);
2834Srgrimes    return -1;
2844Srgrimes  }
2854Srgrimes
2864Srgrimes  *colon = '\0';		/* Cheat the const-ness ! */
2874Srgrimes  res = StrToAddr(str, addr);
2884Srgrimes  *colon = ':';			/* Cheat the const-ness ! */
2894Srgrimes  if (res != 0)
29012515Sphk    return -1;
291
292  return StrToPortRange(colon + 1, low, high, proto);
293}
294
295int
296nat_ProxyRule(struct cmdargs const *arg)
297{
298  char cmd[LINE_LEN];
299  int f, pos;
300  size_t len;
301
302  if (arg->argn >= arg->argc)
303    return -1;
304
305  for (f = arg->argn, pos = 0; f < arg->argc; f++) {
306    len = strlen(arg->argv[f]);
307    if (sizeof cmd - pos < len + (f ? 1 : 0))
308      break;
309    if (f)
310      cmd[pos++] = ' ';
311    strcpy(cmd + pos, arg->argv[f]);
312    pos += len;
313  }
314
315  return PacketAliasProxyRule(cmd);
316}
317
318int
319nat_Pptp(struct cmdargs const *arg)
320{
321  struct in_addr addr;
322
323  if (arg->argc == arg->argn) {
324    addr.s_addr = INADDR_NONE;
325    PacketAliasPptp(addr);
326    return 0;
327  }
328
329  if (arg->argc != arg->argn + 1)
330    return -1;
331
332  addr = GetIpAddr(arg->argv[arg->argn]);
333  if (addr.s_addr == INADDR_NONE) {
334    log_Printf(LogWARN, "%s: invalid address\n", arg->argv[arg->argn]);
335    return 1;
336  }
337
338  PacketAliasPptp(addr);
339  return 0;
340}
341
342int
343nat_SetTarget(struct cmdargs const *arg)
344{
345  struct in_addr addr;
346
347  if (arg->argc == arg->argn) {
348    addr.s_addr = INADDR_ANY;
349    PacketAliasSetTarget(addr);
350    return 0;
351  }
352
353  if (arg->argc != arg->argn + 1)
354    return -1;
355
356  addr = GetIpAddr(arg->argv[arg->argn]);
357  if (addr.s_addr == INADDR_NONE) {
358    log_Printf(LogWARN, "%s: invalid address\n", arg->argv[arg->argn]);
359    return 1;
360  }
361
362  PacketAliasSetTarget(addr);
363  return 0;
364}
365
366static struct mbuf *
367nat_LayerPush(struct bundle *bundle, struct link *l, struct mbuf *bp,
368                int pri, u_short *proto)
369{
370  if (!bundle->NatEnabled || *proto != PROTO_IP)
371    return bp;
372
373  log_Printf(LogDEBUG, "nat_LayerPush: PROTO_IP -> PROTO_IP\n");
374  m_settype(bp, MB_NATOUT);
375  /* Ensure there's a bit of extra buffer for the NAT code... */
376  bp = m_pullup(m_append(bp, NULL, NAT_EXTRABUF));
377  PacketAliasOut(MBUF_CTOP(bp), bp->m_len);
378  bp->m_len = ntohs(((struct ip *)MBUF_CTOP(bp))->ip_len);
379
380  return bp;
381}
382
383static struct mbuf *
384nat_LayerPull(struct bundle *bundle, struct link *l, struct mbuf *bp,
385                u_short *proto)
386{
387  static int gfrags;
388  struct ip *pip, *piip;
389  int ret, len, nfrags;
390  struct mbuf **last;
391  char *fptr;
392
393  if (!bundle->NatEnabled || *proto != PROTO_IP)
394    return bp;
395
396  log_Printf(LogDEBUG, "nat_LayerPull: PROTO_IP -> PROTO_IP\n");
397  m_settype(bp, MB_NATIN);
398  bp = m_pullup(bp);
399  pip = (struct ip *)MBUF_CTOP(bp);
400  piip = (struct ip *)((char *)pip + (pip->ip_hl << 2));
401
402  if (pip->ip_p == IPPROTO_IGMP ||
403      (pip->ip_p == IPPROTO_IPIP && IN_CLASSD(ntohl(piip->ip_dst.s_addr))))
404    return bp;
405
406  /* Ensure there's a bit of extra buffer for the NAT code... */
407  bp = m_pullup(m_append(bp, NULL, NAT_EXTRABUF));
408  ret = PacketAliasIn(MBUF_CTOP(bp), bp->m_len);
409  pip = (struct ip *)MBUF_CTOP(bp);
410
411  bp->m_len = ntohs(pip->ip_len);
412  if (bp->m_len > MAX_MRU) {
413    log_Printf(LogWARN, "nat_LayerPull: Problem with IP header length (%lu)\n",
414               (unsigned long)bp->m_len);
415    m_freem(bp);
416    return NULL;
417  }
418
419  switch (ret) {
420    case PKT_ALIAS_OK:
421      break;
422
423    case PKT_ALIAS_UNRESOLVED_FRAGMENT:
424      /* Save the data for later */
425      fptr = malloc(bp->m_len);
426      bp = mbuf_Read(bp, fptr, bp->m_len);
427      PacketAliasSaveFragment(fptr);
428      log_Printf(LogDEBUG, "Store another frag (%lu) - now %d\n",
429                 (unsigned long)((struct ip *)fptr)->ip_id, ++gfrags);
430      break;
431
432    case PKT_ALIAS_FOUND_HEADER_FRAGMENT:
433      /* Fetch all the saved fragments and chain them on the end of `bp' */
434      last = &bp->m_nextpkt;
435      nfrags = 0;
436      while ((fptr = PacketAliasGetFragment(MBUF_CTOP(bp))) != NULL) {
437        nfrags++;
438        PacketAliasFragmentIn(MBUF_CTOP(bp), fptr);
439        len = ntohs(((struct ip *)fptr)->ip_len);
440        *last = m_get(len, MB_NATIN);
441        memcpy(MBUF_CTOP(*last), fptr, len);
442        free(fptr);
443        last = &(*last)->m_nextpkt;
444      }
445      gfrags -= nfrags;
446      log_Printf(LogDEBUG, "Found a frag header (%lu) - plus %d more frags (no"
447                 "w %d)\n", (unsigned long)((struct ip *)MBUF_CTOP(bp))->ip_id,
448                 nfrags, gfrags);
449      break;
450
451    case PKT_ALIAS_IGNORED:
452      if (log_IsKept(LogTCPIP)) {
453        log_Printf(LogTCPIP, "NAT engine ignored data:\n");
454        PacketCheck(bundle, (char *)pip, ntohs(pip->ip_len), NULL);
455      }
456      m_freem(bp);
457      bp = NULL;
458      break;
459
460    default:
461      log_Printf(LogWARN, "nat_LayerPull: Dropped a packet (%d)....\n", ret);
462      m_freem(bp);
463      bp = NULL;
464      break;
465  }
466
467  return bp;
468}
469
470struct layer natlayer =
471  { LAYER_NAT, "nat", nat_LayerPush, nat_LayerPull };
472