nat_cmd.c revision 60367
1/*-
2 * The code in this file was written by Eivind Eklund <perhaps@yes.no>,
3 * who places it in the public domain without restriction.
4 *
5 * $FreeBSD: head/usr.sbin/ppp/nat_cmd.c 60367 2000-05-11 07:59:21Z brian $
6 */
7
8#include <sys/param.h>
9#include <netinet/in.h>
10#include <arpa/inet.h>
11#include <netdb.h>
12#include <netinet/in_systm.h>
13#include <netinet/in.h>
14#include <netinet/ip.h>
15#include <sys/un.h>
16
17#include <stdio.h>
18#include <stdlib.h>
19#include <string.h>
20#include <termios.h>
21
22#ifdef LOCALNAT
23#include "alias.h"
24#else
25#include <alias.h>
26#endif
27
28#include "layer.h"
29#include "proto.h"
30#include "defs.h"
31#include "command.h"
32#include "log.h"
33#include "nat_cmd.h"
34#include "descriptor.h"
35#include "prompt.h"
36#include "timer.h"
37#include "fsm.h"
38#include "slcompress.h"
39#include "throughput.h"
40#include "iplist.h"
41#include "mbuf.h"
42#include "lqr.h"
43#include "hdlc.h"
44#include "ipcp.h"
45#include "lcp.h"
46#include "ccp.h"
47#include "link.h"
48#include "mp.h"
49#include "filter.h"
50#ifndef NORADIUS
51#include "radius.h"
52#endif
53#include "ip.h"
54#include "bundle.h"
55
56
57#define NAT_EXTRABUF (13)
58
59static int StrToAddr(const char *, struct in_addr *);
60static int StrToPortRange(const char *, u_short *, u_short *, const char *);
61static int StrToAddrAndPort(const char *, struct in_addr *, u_short *,
62                            u_short *, const char *);
63
64static void
65lowhigh(u_short *a, u_short *b)
66{
67  if (a > b) {
68    u_short c;
69
70    c = *b;
71    *b = *a;
72    *a = c;
73  }
74}
75
76int
77nat_RedirectPort(struct cmdargs const *arg)
78{
79  if (!arg->bundle->NatEnabled) {
80    prompt_Printf(arg->prompt, "Alias not enabled\n");
81    return 1;
82  } else if (arg->argc == arg->argn + 3 || arg->argc == arg->argn + 4) {
83    char proto_constant;
84    const char *proto;
85    struct in_addr localaddr;
86    u_short hlocalport, llocalport;
87    struct in_addr aliasaddr;
88    u_short haliasport, laliasport;
89    struct in_addr remoteaddr;
90    u_short hremoteport, lremoteport;
91    struct alias_link *link;
92    int error;
93
94    proto = arg->argv[arg->argn];
95    if (strcmp(proto, "tcp") == 0) {
96      proto_constant = IPPROTO_TCP;
97    } else if (strcmp(proto, "udp") == 0) {
98      proto_constant = IPPROTO_UDP;
99    } else {
100      prompt_Printf(arg->prompt, "port redirect: protocol must be"
101                    " tcp or udp\n");
102      return -1;
103    }
104
105    error = StrToAddrAndPort(arg->argv[arg->argn+1], &localaddr, &llocalport,
106                             &hlocalport, proto);
107    if (error) {
108      prompt_Printf(arg->prompt, "nat port: error reading localaddr:port\n");
109      return -1;
110    }
111
112    error = StrToPortRange(arg->argv[arg->argn+2], &laliasport, &haliasport,
113                           proto);
114    if (error) {
115      prompt_Printf(arg->prompt, "nat port: error reading alias port\n");
116      return -1;
117    }
118    aliasaddr.s_addr = INADDR_ANY;
119
120    if (arg->argc == arg->argn + 4) {
121      error = StrToAddrAndPort(arg->argv[arg->argn+3], &remoteaddr,
122                               &lremoteport, &hremoteport, proto);
123      if (error) {
124        prompt_Printf(arg->prompt, "nat port: error reading "
125                      "remoteaddr:port\n");
126        return -1;
127      }
128    } else {
129      remoteaddr.s_addr = INADDR_ANY;
130      lremoteport = hremoteport = 0;
131    }
132
133    lowhigh(&llocalport, &hlocalport);
134    lowhigh(&laliasport, &haliasport);
135    lowhigh(&lremoteport, &hremoteport);
136
137    if (haliasport - laliasport != hlocalport - llocalport) {
138      prompt_Printf(arg->prompt, "nat port: local & alias port ranges "
139                    "are not equal\n");
140      return -1;
141    }
142
143    if (hremoteport && hremoteport - lremoteport != hlocalport - llocalport) {
144      prompt_Printf(arg->prompt, "nat port: local & remote port ranges "
145                    "are not equal\n");
146      return -1;
147    }
148
149    while (laliasport <= haliasport) {
150      link = PacketAliasRedirectPort(localaddr, htons(llocalport),
151				     remoteaddr, htons(lremoteport),
152                                     aliasaddr, htons(laliasport),
153				     proto_constant);
154
155      if (link == NULL) {
156        prompt_Printf(arg->prompt, "nat port: %d: error %d\n", laliasport,
157                      error);
158        return 1;
159      }
160      llocalport++;
161      laliasport++;
162      if (hremoteport)
163        lremoteport++;
164    }
165
166    return 0;
167  }
168
169  return -1;
170}
171
172
173int
174nat_RedirectAddr(struct cmdargs const *arg)
175{
176  if (!arg->bundle->NatEnabled) {
177    prompt_Printf(arg->prompt, "nat not enabled\n");
178    return 1;
179  } else if (arg->argc == arg->argn+2) {
180    int error;
181    struct in_addr localaddr, aliasaddr;
182    struct alias_link *link;
183
184    error = StrToAddr(arg->argv[arg->argn], &localaddr);
185    if (error) {
186      prompt_Printf(arg->prompt, "address redirect: invalid local address\n");
187      return 1;
188    }
189    error = StrToAddr(arg->argv[arg->argn+1], &aliasaddr);
190    if (error) {
191      prompt_Printf(arg->prompt, "address redirect: invalid alias address\n");
192      prompt_Printf(arg->prompt, "Usage: nat %s %s\n", arg->cmd->name,
193                    arg->cmd->syntax);
194      return 1;
195    }
196    link = PacketAliasRedirectAddr(localaddr, aliasaddr);
197    if (link == NULL) {
198      prompt_Printf(arg->prompt, "address redirect: packet aliasing"
199                    " engine error\n");
200      prompt_Printf(arg->prompt, "Usage: nat %s %s\n", arg->cmd->name,
201                    arg->cmd->syntax);
202    }
203  } else
204    return -1;
205
206  return 0;
207}
208
209
210static int
211StrToAddr(const char *str, struct in_addr *addr)
212{
213  struct hostent *hp;
214
215  if (inet_aton(str, addr))
216    return 0;
217
218  hp = gethostbyname(str);
219  if (!hp) {
220    log_Printf(LogWARN, "StrToAddr: Unknown host %s.\n", str);
221    return -1;
222  }
223  *addr = *((struct in_addr *) hp->h_addr);
224  return 0;
225}
226
227
228static int
229StrToPort(const char *str, u_short *port, const char *proto)
230{
231  struct servent *sp;
232  char *end;
233
234  *port = strtol(str, &end, 10);
235  if (*end != '\0') {
236    sp = getservbyname(str, proto);
237    if (sp == NULL) {
238      log_Printf(LogWARN, "StrToAddr: Unknown port or service %s/%s.\n",
239	        str, proto);
240      return -1;
241    }
242    *port = ntohs(sp->s_port);
243  }
244
245  return 0;
246}
247
248static int
249StrToPortRange(const char *str, u_short *low, u_short *high, const char *proto)
250{
251  char *minus;
252  int res;
253
254  minus = strchr(str, '-');
255  if (minus)
256    *minus = '\0';		/* Cheat the const-ness ! */
257
258  res = StrToPort(str, low, proto);
259
260  if (minus)
261    *minus = '-';		/* Cheat the const-ness ! */
262
263  if (res == 0) {
264    if (minus)
265      res = StrToPort(minus + 1, high, proto);
266    else
267      *high = *low;
268  }
269
270  return res;
271}
272
273static int
274StrToAddrAndPort(const char *str, struct in_addr *addr, u_short *low,
275                 u_short *high, const char *proto)
276{
277  char *colon;
278  int res;
279
280  colon = strchr(str, ':');
281  if (!colon) {
282    log_Printf(LogWARN, "StrToAddrAndPort: %s is missing port number.\n", str);
283    return -1;
284  }
285
286  *colon = '\0';		/* Cheat the const-ness ! */
287  res = StrToAddr(str, addr);
288  *colon = ':';			/* Cheat the const-ness ! */
289  if (res != 0)
290    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  if (!strcasecmp(arg->argv[arg->argn], "MYADDR")) {
357    addr.s_addr = INADDR_ANY;
358    PacketAliasSetTarget(addr);
359    return 0;
360  }
361
362  addr = GetIpAddr(arg->argv[arg->argn]);
363  if (addr.s_addr == INADDR_NONE) {
364    log_Printf(LogWARN, "%s: invalid address\n", arg->argv[arg->argn]);
365    return 1;
366  }
367
368  PacketAliasSetTarget(addr);
369  return 0;
370}
371
372static struct mbuf *
373nat_LayerPush(struct bundle *bundle, struct link *l, struct mbuf *bp,
374                int pri, u_short *proto)
375{
376  if (!bundle->NatEnabled || *proto != PROTO_IP)
377    return bp;
378
379  log_Printf(LogDEBUG, "nat_LayerPush: PROTO_IP -> PROTO_IP\n");
380  m_settype(bp, MB_NATOUT);
381  /* Ensure there's a bit of extra buffer for the NAT code... */
382  bp = m_pullup(m_append(bp, NULL, NAT_EXTRABUF));
383  PacketAliasOut(MBUF_CTOP(bp), bp->m_len);
384  bp->m_len = ntohs(((struct ip *)MBUF_CTOP(bp))->ip_len);
385
386  return bp;
387}
388
389static struct mbuf *
390nat_LayerPull(struct bundle *bundle, struct link *l, struct mbuf *bp,
391                u_short *proto)
392{
393  static int gfrags;
394  struct ip *pip, *piip;
395  int ret, len, nfrags;
396  struct mbuf **last;
397  char *fptr;
398
399  if (!bundle->NatEnabled || *proto != PROTO_IP)
400    return bp;
401
402  log_Printf(LogDEBUG, "nat_LayerPull: PROTO_IP -> PROTO_IP\n");
403  m_settype(bp, MB_NATIN);
404  bp = m_pullup(bp);
405  pip = (struct ip *)MBUF_CTOP(bp);
406  piip = (struct ip *)((char *)pip + (pip->ip_hl << 2));
407
408  if (pip->ip_p == IPPROTO_IGMP ||
409      (pip->ip_p == IPPROTO_IPIP && IN_CLASSD(ntohl(piip->ip_dst.s_addr))))
410    return bp;
411
412  /* Ensure there's a bit of extra buffer for the NAT code... */
413  bp = m_pullup(m_append(bp, NULL, NAT_EXTRABUF));
414  ret = PacketAliasIn(MBUF_CTOP(bp), bp->m_len);
415  pip = (struct ip *)MBUF_CTOP(bp);
416
417  bp->m_len = ntohs(pip->ip_len);
418  if (bp->m_len > MAX_MRU) {
419    log_Printf(LogWARN, "nat_LayerPull: Problem with IP header length (%lu)\n",
420               (unsigned long)bp->m_len);
421    m_freem(bp);
422    return NULL;
423  }
424
425  switch (ret) {
426    case PKT_ALIAS_OK:
427      break;
428
429    case PKT_ALIAS_UNRESOLVED_FRAGMENT:
430      /* Save the data for later */
431      fptr = malloc(bp->m_len);
432      bp = mbuf_Read(bp, fptr, bp->m_len);
433      PacketAliasSaveFragment(fptr);
434      log_Printf(LogDEBUG, "Store another frag (%lu) - now %d\n",
435                 (unsigned long)((struct ip *)fptr)->ip_id, ++gfrags);
436      break;
437
438    case PKT_ALIAS_FOUND_HEADER_FRAGMENT:
439      /* Fetch all the saved fragments and chain them on the end of `bp' */
440      last = &bp->m_nextpkt;
441      nfrags = 0;
442      while ((fptr = PacketAliasGetFragment(MBUF_CTOP(bp))) != NULL) {
443        nfrags++;
444        PacketAliasFragmentIn(MBUF_CTOP(bp), fptr);
445        len = ntohs(((struct ip *)fptr)->ip_len);
446        *last = m_get(len, MB_NATIN);
447        memcpy(MBUF_CTOP(*last), fptr, len);
448        free(fptr);
449        last = &(*last)->m_nextpkt;
450      }
451      gfrags -= nfrags;
452      log_Printf(LogDEBUG, "Found a frag header (%lu) - plus %d more frags (no"
453                 "w %d)\n", (unsigned long)((struct ip *)MBUF_CTOP(bp))->ip_id,
454                 nfrags, gfrags);
455      break;
456
457    case PKT_ALIAS_IGNORED:
458      if (log_IsKept(LogTCPIP)) {
459        log_Printf(LogTCPIP, "NAT engine ignored data:\n");
460        PacketCheck(bundle, (char *)pip, ntohs(pip->ip_len), NULL);
461      }
462      m_freem(bp);
463      bp = NULL;
464      break;
465
466    default:
467      log_Printf(LogWARN, "nat_LayerPull: Dropped a packet (%d)....\n", ret);
468      m_freem(bp);
469      bp = NULL;
470      break;
471  }
472
473  return bp;
474}
475
476struct layer natlayer =
477  { LAYER_NAT, "nat", nat_LayerPush, nat_LayerPull };
478