ng_nat.c revision 146063
1/*- 2 * Copyright 2005, Gleb Smirnoff <glebius@FreeBSD.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 * $FreeBSD: head/sys/netgraph/ng_nat.c 146063 2005-05-10 14:19:10Z glebius $ 27 */ 28 29#include <sys/param.h> 30#include <sys/systm.h> 31#include <sys/kernel.h> 32#include <sys/mbuf.h> 33#include <sys/malloc.h> 34#include <sys/ctype.h> 35#include <sys/errno.h> 36#include <sys/syslog.h> 37 38#include <netinet/in_systm.h> 39#include <netinet/in.h> 40#include <netinet/ip.h> 41#include <netinet/ip_icmp.h> 42#include <netinet/tcp.h> 43#include <netinet/udp.h> 44 45#include <netinet/libalias/alias.h> 46 47#include <netgraph/ng_message.h> 48#include <netgraph/ng_parse.h> 49#include <netgraph/ng_nat.h> 50#include <netgraph/netgraph.h> 51 52static ng_constructor_t ng_nat_constructor; 53static ng_rcvmsg_t ng_nat_rcvmsg; 54static ng_shutdown_t ng_nat_shutdown; 55static ng_newhook_t ng_nat_newhook; 56static ng_rcvdata_t ng_nat_rcvdata; 57static ng_disconnect_t ng_nat_disconnect; 58 59static struct mbuf * m_megapullup(struct mbuf *, int); 60 61/* List of commands and how to convert arguments to/from ASCII. */ 62static const struct ng_cmdlist ng_nat_cmdlist[] = { 63 { 64 NGM_NAT_COOKIE, 65 NGM_NAT_SET_IPADDR, 66 "setaliasaddr", 67 &ng_parse_ipaddr_type, 68 NULL 69 }, 70 { 0 } 71}; 72 73/* Netgraph node type descriptor. */ 74static struct ng_type typestruct = { 75 .version = NG_ABI_VERSION, 76 .name = NG_NAT_NODE_TYPE, 77 .constructor = ng_nat_constructor, 78 .rcvmsg = ng_nat_rcvmsg, 79 .shutdown = ng_nat_shutdown, 80 .newhook = ng_nat_newhook, 81 .rcvdata = ng_nat_rcvdata, 82 .disconnect = ng_nat_disconnect, 83 .cmdlist = ng_nat_cmdlist, 84}; 85NETGRAPH_INIT(nat, &typestruct); 86MODULE_DEPEND(ng_nat, libalias, 1, 1, 1); 87 88/* Information we store for each node. */ 89struct ng_priv_priv { 90 node_p node; /* back pointer to node */ 91 hook_p in; /* hook for demasquerading */ 92 hook_p out; /* hook for masquerading */ 93 struct libalias *lib; /* libalias handler */ 94 uint32_t flags; /* status flags */ 95}; 96typedef struct ng_priv_priv *priv_p; 97 98/* Values of flags */ 99#define NGNAT_READY 0x1 /* We have everything to work */ 100#define NGNAT_ADDR_DEFINED 0x2 /* NGM_NAT_SET_IPADDR happened */ 101 102static int 103ng_nat_constructor(node_p node) 104{ 105 priv_p priv; 106 107 /* Initialize private descriptor. */ 108 MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH, 109 M_NOWAIT | M_ZERO); 110 if (priv == NULL) 111 return (ENOMEM); 112 113 /* Init aliasing engine. */ 114 priv->lib = LibAliasInit(NULL); 115 if (priv->lib == NULL) { 116 FREE(priv, M_NETGRAPH); 117 return (ENOMEM); 118 } 119 120 /* Set same ports on. */ 121 (void )LibAliasSetMode(priv->lib, PKT_ALIAS_SAME_PORTS, 122 PKT_ALIAS_SAME_PORTS); 123 124 /* Link structs together. */ 125 NG_NODE_SET_PRIVATE(node, priv); 126 priv->node = node; 127 128 /* 129 * libalias is not thread safe, so our node 130 * must be single threaded. 131 */ 132 NG_NODE_FORCE_WRITER(node); 133 134 return (0); 135} 136 137static int 138ng_nat_newhook(node_p node, hook_p hook, const char *name) 139{ 140 const priv_p priv = NG_NODE_PRIVATE(node); 141 142 if (strcmp(name, NG_NAT_HOOK_IN) == 0) { 143 priv->in = hook; 144 } else if (strcmp(name, NG_NAT_HOOK_OUT) == 0) { 145 priv->out = hook; 146 } else 147 return (EINVAL); 148 149 if (priv->out != NULL && 150 priv->in != NULL && 151 priv->flags & NGNAT_ADDR_DEFINED) 152 priv->flags |= NGNAT_READY; 153 154 return(0); 155} 156 157static int 158ng_nat_rcvmsg(node_p node, item_p item, hook_p lasthook) 159{ 160 const priv_p priv = NG_NODE_PRIVATE(node); 161 struct ng_mesg *resp = NULL; 162 struct ng_mesg *msg; 163 int error = 0; 164 165 NGI_GET_MSG(item, msg); 166 167 switch (msg->header.typecookie) { 168 case NGM_NAT_COOKIE: 169 switch (msg->header.cmd) { 170 case NGM_NAT_SET_IPADDR: 171 { 172 struct in_addr *const ia = (struct in_addr *)msg->data; 173 174 if (msg->header.arglen < sizeof(*ia)) { 175 error = EINVAL; 176 break; 177 } 178 179 LibAliasSetAddress(priv->lib, *ia); 180 181 priv->flags |= NGNAT_ADDR_DEFINED; 182 if (priv->out != NULL && 183 priv->in != NULL) 184 priv->flags |= NGNAT_READY; 185 } 186 break; 187 default: 188 error = EINVAL; /* unknown command */ 189 break; 190 } 191 break; 192 default: 193 error = EINVAL; /* unknown cookie type */ 194 break; 195 } 196 197 NG_RESPOND_MSG(error, node, item, resp); 198 NG_FREE_MSG(msg); 199 return (error); 200} 201 202static int 203ng_nat_rcvdata(hook_p hook, item_p item ) 204{ 205 const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 206 struct mbuf *m; 207 struct ip *ip; 208 int rval, error = 0; 209 char *c; 210 211 if (!(priv->flags & NGNAT_READY)) { 212 NG_FREE_ITEM(item); 213 return (ENXIO); 214 } 215 216 m = NGI_M(item); 217 218 if ((m = m_megapullup(m, m->m_pkthdr.len)) == NULL) { 219 NGI_M(item) = NULL; /* avoid double free */ 220 NG_FREE_ITEM(item); 221 return (ENOBUFS); 222 } 223 224 NGI_M(item) = m; 225 226 KASSERT(m->m_pkthdr.len == ntohs(ip->ip_len), 227 ("ng_nat: ip_len != m_pkthdr.len")); 228 229 c = mtod(m, char *); 230 ip = mtod(m, struct ip *); 231 232 if (hook == priv->in) { 233 rval = LibAliasIn(priv->lib, c, MCLBYTES); 234 if (rval != PKT_ALIAS_OK) { 235 printf("in %u\n", rval); 236 NG_FREE_ITEM(item); 237 return (EINVAL); 238 } 239 m->m_pkthdr.len = m->m_len = ntohs(ip->ip_len); 240 NG_FWD_ITEM_HOOK(error, item, priv->out); 241 } else if (hook == priv->out) { 242 rval = LibAliasOut(priv->lib, c, MCLBYTES); 243 if (rval != PKT_ALIAS_OK) { 244 printf("out %u\n", rval); 245 NG_FREE_ITEM(item); 246 return (EINVAL); 247 } 248 m->m_pkthdr.len = m->m_len = ntohs(ip->ip_len); 249 NG_FWD_ITEM_HOOK(error, item, priv->in); 250 } else 251 panic("ng_nat: unknown hook!\n"); 252 253 return (error); 254} 255 256static int 257ng_nat_shutdown(node_p node) 258{ 259 const priv_p priv = NG_NODE_PRIVATE(node); 260 261 NG_NODE_SET_PRIVATE(node, NULL); 262 NG_NODE_UNREF(node); 263 LibAliasUninit(priv->lib); 264 FREE(priv, M_NETGRAPH); 265 266 return (0); 267} 268 269static int 270ng_nat_disconnect(hook_p hook) 271{ 272 const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 273 274 priv->flags &= ~NGNAT_READY; 275 276 if (hook == priv->out) 277 priv->out = NULL; 278 if (hook == priv->in) 279 priv->in = NULL; 280 281 if (priv->out == NULL && priv->in == NULL) 282 ng_rmnode_self(NG_HOOK_NODE(hook)); 283 284 return (0); 285} 286 287/* 288 * m_megapullup() function is a big hack. 289 * 290 * It allocates an mbuf with cluster and copies the whole 291 * chain into cluster, so that it is all contigous and the 292 * whole packet can be accessed via char pointer. 293 * 294 * This is required, because libalias doesn't have idea 295 * about mbufs. 296 */ 297static struct mbuf * 298m_megapullup(struct mbuf *m, int len) 299{ 300 struct mbuf *mcl; 301 caddr_t cp; 302 303 if (len > MCLBYTES) 304 goto bad; 305 306 if ((mcl = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR)) == NULL) 307 goto bad; 308 309 cp = mtod(mcl, caddr_t); 310 m_copydata(m, 0, len, cp); 311 m_move_pkthdr(mcl, m); 312 mcl->m_len = mcl->m_pkthdr.len; 313 m_freem(m); 314 315 return (mcl); 316bad: 317 m_freem(m); 318 return (NULL); 319} 320