nat_cmd.c revision 58776
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 58776 2000-03-29 09:31:52Z 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
342static struct mbuf *
343nat_LayerPush(struct bundle *bundle, struct link *l, struct mbuf *bp,
344                int pri, u_short *proto)
345{
346  if (!bundle->NatEnabled || *proto != PROTO_IP)
347    return bp;
348
349  log_Printf(LogDEBUG, "nat_LayerPush: PROTO_IP -> PROTO_IP\n");
350  m_settype(bp, MB_NATOUT);
351  /* Ensure there's a bit of extra buffer for the NAT code... */
352  bp = m_pullup(m_append(bp, NULL, NAT_EXTRABUF));
353  PacketAliasOut(MBUF_CTOP(bp), bp->m_len);
354  bp->m_len = ntohs(((struct ip *)MBUF_CTOP(bp))->ip_len);
355
356  return bp;
357}
358
359static struct mbuf *
360nat_LayerPull(struct bundle *bundle, struct link *l, struct mbuf *bp,
361                u_short *proto)
362{
363  static int gfrags;
364  struct ip *pip, *piip;
365  int ret, len, nfrags;
366  struct mbuf **last;
367  char *fptr;
368
369  if (!bundle->NatEnabled || *proto != PROTO_IP)
370    return bp;
371
372  log_Printf(LogDEBUG, "nat_LayerPull: PROTO_IP -> PROTO_IP\n");
373  m_settype(bp, MB_NATIN);
374  bp = m_pullup(bp);
375  pip = (struct ip *)MBUF_CTOP(bp);
376  piip = (struct ip *)((char *)pip + (pip->ip_hl << 2));
377
378  if (pip->ip_p == IPPROTO_IGMP ||
379      (pip->ip_p == IPPROTO_IPIP && IN_CLASSD(ntohl(piip->ip_dst.s_addr))))
380    return bp;
381
382  /* Ensure there's a bit of extra buffer for the NAT code... */
383  bp = m_pullup(m_append(bp, NULL, NAT_EXTRABUF));
384  ret = PacketAliasIn(MBUF_CTOP(bp), bp->m_len);
385  pip = (struct ip *)MBUF_CTOP(bp);
386
387  bp->m_len = ntohs(pip->ip_len);
388  if (bp->m_len > MAX_MRU) {
389    log_Printf(LogWARN, "nat_LayerPull: Problem with IP header length (%lu)\n",
390               (unsigned long)bp->m_len);
391    m_freem(bp);
392    return NULL;
393  }
394
395  switch (ret) {
396    case PKT_ALIAS_OK:
397      break;
398
399    case PKT_ALIAS_UNRESOLVED_FRAGMENT:
400      /* Save the data for later */
401      fptr = malloc(bp->m_len);
402      bp = mbuf_Read(bp, fptr, bp->m_len);
403      PacketAliasSaveFragment(fptr);
404      log_Printf(LogDEBUG, "Store another frag (%lu) - now %d\n",
405                 (unsigned long)((struct ip *)fptr)->ip_id, ++gfrags);
406      break;
407
408    case PKT_ALIAS_FOUND_HEADER_FRAGMENT:
409      /* Fetch all the saved fragments and chain them on the end of `bp' */
410      last = &bp->m_nextpkt;
411      nfrags = 0;
412      while ((fptr = PacketAliasGetFragment(MBUF_CTOP(bp))) != NULL) {
413        nfrags++;
414        PacketAliasFragmentIn(MBUF_CTOP(bp), fptr);
415        len = ntohs(((struct ip *)fptr)->ip_len);
416        *last = m_get(len, MB_NATIN);
417        memcpy(MBUF_CTOP(*last), fptr, len);
418        free(fptr);
419        last = &(*last)->m_nextpkt;
420      }
421      gfrags -= nfrags;
422      log_Printf(LogDEBUG, "Found a frag header (%lu) - plus %d more frags (no"
423                 "w %d)\n", (unsigned long)((struct ip *)MBUF_CTOP(bp))->ip_id,
424                 nfrags, gfrags);
425      break;
426
427    case PKT_ALIAS_IGNORED:
428      if (log_IsKept(LogTCPIP)) {
429        log_Printf(LogTCPIP, "NAT engine ignored data:\n");
430        PacketCheck(bundle, (char *)pip, ntohs(pip->ip_len), NULL);
431      }
432      m_freem(bp);
433      bp = NULL;
434      break;
435
436    default:
437      log_Printf(LogWARN, "nat_LayerPull: Dropped a packet (%d)....\n", ret);
438      m_freem(bp);
439      bp = NULL;
440      break;
441  }
442
443  return bp;
444}
445
446struct layer natlayer =
447  { LAYER_NAT, "nat", nat_LayerPush, nat_LayerPull };
448