diff options
| author | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-22 10:38:37 -0500 |
|---|---|---|
| committer | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-22 10:38:37 -0500 |
| commit | fcc9d2e5a6c89d22b8b773a64fb4ad21ac318446 (patch) | |
| tree | a57612d1888735a2ec7972891b68c1ac5ec8faea /net/ipv4/netfilter | |
| parent | 8dea78da5cee153b8af9c07a2745f6c55057fe12 (diff) | |
Diffstat (limited to 'net/ipv4/netfilter')
21 files changed, 4848 insertions, 0 deletions
diff --git a/net/ipv4/netfilter/ip_queue.c b/net/ipv4/netfilter/ip_queue.c new file mode 100644 index 00000000000..e59aabd0eae --- /dev/null +++ b/net/ipv4/netfilter/ip_queue.c | |||
| @@ -0,0 +1,637 @@ | |||
| 1 | /* | ||
| 2 | * This is a module which is used for queueing IPv4 packets and | ||
| 3 | * communicating with userspace via netlink. | ||
| 4 | * | ||
| 5 | * (C) 2000-2002 James Morris <jmorris@intercode.com.au> | ||
| 6 | * (C) 2003-2005 Netfilter Core Team <coreteam@netfilter.org> | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or modify | ||
| 9 | * it under the terms of the GNU General Public License version 2 as | ||
| 10 | * published by the Free Software Foundation. | ||
| 11 | */ | ||
| 12 | #include <linux/module.h> | ||
| 13 | #include <linux/skbuff.h> | ||
| 14 | #include <linux/init.h> | ||
| 15 | #include <linux/ip.h> | ||
| 16 | #include <linux/notifier.h> | ||
| 17 | #include <linux/netdevice.h> | ||
| 18 | #include <linux/netfilter.h> | ||
| 19 | #include <linux/netfilter_ipv4/ip_queue.h> | ||
| 20 | #include <linux/netfilter_ipv4/ip_tables.h> | ||
| 21 | #include <linux/netlink.h> | ||
| 22 | #include <linux/spinlock.h> | ||
| 23 | #include <linux/sysctl.h> | ||
| 24 | #include <linux/proc_fs.h> | ||
| 25 | #include <linux/seq_file.h> | ||
| 26 | #include <linux/security.h> | ||
| 27 | #include <linux/net.h> | ||
| 28 | #include <linux/mutex.h> | ||
| 29 | #include <linux/slab.h> | ||
| 30 | #include <net/net_namespace.h> | ||
| 31 | #include <net/sock.h> | ||
| 32 | #include <net/route.h> | ||
| 33 | #include <net/netfilter/nf_queue.h> | ||
| 34 | #include <net/ip.h> | ||
| 35 | |||
| 36 | #define IPQ_QMAX_DEFAULT 1024 | ||
| 37 | #define IPQ_PROC_FS_NAME "ip_queue" | ||
| 38 | #define NET_IPQ_QMAX 2088 | ||
| 39 | #define NET_IPQ_QMAX_NAME "ip_queue_maxlen" | ||
| 40 | |||
| 41 | typedef int (*ipq_cmpfn)(struct nf_queue_entry *, unsigned long); | ||
| 42 | |||
| 43 | static unsigned char copy_mode __read_mostly = IPQ_COPY_NONE; | ||
| 44 | static unsigned int queue_maxlen __read_mostly = IPQ_QMAX_DEFAULT; | ||
| 45 | static DEFINE_SPINLOCK(queue_lock); | ||
| 46 | static int peer_pid __read_mostly; | ||
| 47 | static unsigned int copy_range __read_mostly; | ||
| 48 | static unsigned int queue_total; | ||
| 49 | static unsigned int queue_dropped = 0; | ||
| 50 | static unsigned int queue_user_dropped = 0; | ||
| 51 | static struct sock *ipqnl __read_mostly; | ||
| 52 | static LIST_HEAD(queue_list); | ||
| 53 | static DEFINE_MUTEX(ipqnl_mutex); | ||
| 54 | |||
| 55 | static inline void | ||
| 56 | __ipq_enqueue_entry(struct nf_queue_entry *entry) | ||
| 57 | { | ||
| 58 | list_add_tail(&entry->list, &queue_list); | ||
| 59 | queue_total++; | ||
| 60 | } | ||
| 61 | |||
| 62 | static inline int | ||
| 63 | __ipq_set_mode(unsigned char mode, unsigned int range) | ||
| 64 | { | ||
| 65 | int status = 0; | ||
| 66 | |||
| 67 | switch(mode) { | ||
| 68 | case IPQ_COPY_NONE: | ||
| 69 | case IPQ_COPY_META: | ||
| 70 | copy_mode = mode; | ||
| 71 | copy_range = 0; | ||
| 72 | break; | ||
| 73 | |||
| 74 | case IPQ_COPY_PACKET: | ||
| 75 | if (range > 0xFFFF) | ||
| 76 | range = 0xFFFF; | ||
| 77 | copy_range = range; | ||
| 78 | copy_mode = mode; | ||
| 79 | break; | ||
| 80 | |||
| 81 | default: | ||
| 82 | status = -EINVAL; | ||
| 83 | |||
| 84 | } | ||
| 85 | return status; | ||
| 86 | } | ||
| 87 | |||
| 88 | static void __ipq_flush(ipq_cmpfn cmpfn, unsigned long data); | ||
| 89 | |||
| 90 | static inline void | ||
| 91 | __ipq_reset(void) | ||
| 92 | { | ||
| 93 | peer_pid = 0; | ||
| 94 | net_disable_timestamp(); | ||
| 95 | __ipq_set_mode(IPQ_COPY_NONE, 0); | ||
| 96 | __ipq_flush(NULL, 0); | ||
| 97 | } | ||
| 98 | |||
| 99 | static struct nf_queue_entry * | ||
| 100 | ipq_find_dequeue_entry(unsigned long id) | ||
| 101 | { | ||
| 102 | struct nf_queue_entry *entry = NULL, *i; | ||
| 103 | |||
| 104 | spin_lock_bh(&queue_lock); | ||
| 105 | |||
| 106 | list_for_each_entry(i, &queue_list, list) { | ||
| 107 | if ((unsigned long)i == id) { | ||
| 108 | entry = i; | ||
| 109 | break; | ||
| 110 | } | ||
| 111 | } | ||
| 112 | |||
| 113 | if (entry) { | ||
| 114 | list_del(&entry->list); | ||
| 115 | queue_total--; | ||
| 116 | } | ||
| 117 | |||
| 118 | spin_unlock_bh(&queue_lock); | ||
| 119 | return entry; | ||
| 120 | } | ||
| 121 | |||
| 122 | static void | ||
| 123 | __ipq_flush(ipq_cmpfn cmpfn, unsigned long data) | ||
| 124 | { | ||
| 125 | struct nf_queue_entry *entry, *next; | ||
| 126 | |||
| 127 | list_for_each_entry_safe(entry, next, &queue_list, list) { | ||
| 128 | if (!cmpfn || cmpfn(entry, data)) { | ||
| 129 | list_del(&entry->list); | ||
| 130 | queue_total--; | ||
| 131 | nf_reinject(entry, NF_DROP); | ||
| 132 | } | ||
| 133 | } | ||
| 134 | } | ||
| 135 | |||
| 136 | static void | ||
| 137 | ipq_flush(ipq_cmpfn cmpfn, unsigned long data) | ||
| 138 | { | ||
| 139 | spin_lock_bh(&queue_lock); | ||
| 140 | __ipq_flush(cmpfn, data); | ||
| 141 | spin_unlock_bh(&queue_lock); | ||
| 142 | } | ||
| 143 | |||
| 144 | static struct sk_buff * | ||
| 145 | ipq_build_packet_message(struct nf_queue_entry *entry, int *errp) | ||
| 146 | { | ||
| 147 | sk_buff_data_t old_tail; | ||
| 148 | size_t size = 0; | ||
| 149 | size_t data_len = 0; | ||
| 150 | struct sk_buff *skb; | ||
| 151 | struct ipq_packet_msg *pmsg; | ||
| 152 | struct nlmsghdr *nlh; | ||
| 153 | struct timeval tv; | ||
| 154 | |||
| 155 | switch (ACCESS_ONCE(copy_mode)) { | ||
| 156 | case IPQ_COPY_META: | ||
| 157 | case IPQ_COPY_NONE: | ||
| 158 | size = NLMSG_SPACE(sizeof(*pmsg)); | ||
| 159 | break; | ||
| 160 | |||
| 161 | case IPQ_COPY_PACKET: | ||
| 162 | if (entry->skb->ip_summed == CHECKSUM_PARTIAL && | ||
| 163 | (*errp = skb_checksum_help(entry->skb))) | ||
| 164 | return NULL; | ||
| 165 | |||
| 166 | data_len = ACCESS_ONCE(copy_range); | ||
| 167 | if (data_len == 0 || data_len > entry->skb->len) | ||
| 168 | data_len = entry->skb->len; | ||
| 169 | |||
| 170 | size = NLMSG_SPACE(sizeof(*pmsg) + data_len); | ||
| 171 | break; | ||
| 172 | |||
| 173 | default: | ||
| 174 | *errp = -EINVAL; | ||
| 175 | return NULL; | ||
| 176 | } | ||
| 177 | |||
| 178 | skb = alloc_skb(size, GFP_ATOMIC); | ||
| 179 | if (!skb) | ||
| 180 | goto nlmsg_failure; | ||
| 181 | |||
| 182 | old_tail = skb->tail; | ||
| 183 | nlh = NLMSG_PUT(skb, 0, 0, IPQM_PACKET, size - sizeof(*nlh)); | ||
| 184 | pmsg = NLMSG_DATA(nlh); | ||
| 185 | memset(pmsg, 0, sizeof(*pmsg)); | ||
| 186 | |||
| 187 | pmsg->packet_id = (unsigned long )entry; | ||
| 188 | pmsg->data_len = data_len; | ||
| 189 | tv = ktime_to_timeval(entry->skb->tstamp); | ||
| 190 | pmsg->timestamp_sec = tv.tv_sec; | ||
| 191 | pmsg->timestamp_usec = tv.tv_usec; | ||
| 192 | pmsg->mark = entry->skb->mark; | ||
| 193 | pmsg->hook = entry->hook; | ||
| 194 | pmsg->hw_protocol = entry->skb->protocol; | ||
| 195 | |||
| 196 | if (entry->indev) | ||
| 197 | strcpy(pmsg->indev_name, entry->indev->name); | ||
| 198 | else | ||
| 199 | pmsg->indev_name[0] = '\0'; | ||
| 200 | |||
| 201 | if (entry->outdev) | ||
| 202 | strcpy(pmsg->outdev_name, entry->outdev->name); | ||
| 203 | else | ||
| 204 | pmsg->outdev_name[0] = '\0'; | ||
| 205 | |||
| 206 | if (entry->indev && entry->skb->dev && | ||
| 207 | entry->skb->mac_header != entry->skb->network_header) { | ||
| 208 | pmsg->hw_type = entry->skb->dev->type; | ||
| 209 | pmsg->hw_addrlen = dev_parse_header(entry->skb, | ||
| 210 | pmsg->hw_addr); | ||
| 211 | } | ||
| 212 | |||
| 213 | if (data_len) | ||
| 214 | if (skb_copy_bits(entry->skb, 0, pmsg->payload, data_len)) | ||
| 215 | BUG(); | ||
| 216 | |||
| 217 | nlh->nlmsg_len = skb->tail - old_tail; | ||
| 218 | return skb; | ||
| 219 | |||
| 220 | nlmsg_failure: | ||
| 221 | kfree_skb(skb); | ||
| 222 | *errp = -EINVAL; | ||
| 223 | printk(KERN_ERR "ip_queue: error creating packet message\n"); | ||
| 224 | return NULL; | ||
| 225 | } | ||
| 226 | |||
| 227 | static int | ||
| 228 | ipq_enqueue_packet(struct nf_queue_entry *entry, unsigned int queuenum) | ||
| 229 | { | ||
| 230 | int status = -EINVAL; | ||
| 231 | struct sk_buff *nskb; | ||
| 232 | |||
| 233 | if (copy_mode == IPQ_COPY_NONE) | ||
| 234 | return -EAGAIN; | ||
| 235 | |||
| 236 | nskb = ipq_build_packet_message(entry, &status); | ||
| 237 | if (nskb == NULL) | ||
| 238 | return status; | ||
| 239 | |||
| 240 | spin_lock_bh(&queue_lock); | ||
| 241 | |||
| 242 | if (!peer_pid) | ||
| 243 | goto err_out_free_nskb; | ||
| 244 | |||
| 245 | if (queue_total >= queue_maxlen) { | ||
| 246 | queue_dropped++; | ||
| 247 | status = -ENOSPC; | ||
| 248 | if (net_ratelimit()) | ||
| 249 | printk (KERN_WARNING "ip_queue: full at %d entries, " | ||
| 250 | "dropping packets(s). Dropped: %d\n", queue_total, | ||
| 251 | queue_dropped); | ||
| 252 | goto err_out_free_nskb; | ||
| 253 | } | ||
| 254 | |||
| 255 | /* netlink_unicast will either free the nskb or attach it to a socket */ | ||
| 256 | status = netlink_unicast(ipqnl, nskb, peer_pid, MSG_DONTWAIT); | ||
| 257 | if (status < 0) { | ||
| 258 | queue_user_dropped++; | ||
| 259 | goto err_out_unlock; | ||
| 260 | } | ||
| 261 | |||
| 262 | __ipq_enqueue_entry(entry); | ||
| 263 | |||
| 264 | spin_unlock_bh(&queue_lock); | ||
| 265 | return status; | ||
| 266 | |||
| 267 | err_out_free_nskb: | ||
| 268 | kfree_skb(nskb); | ||
| 269 | |||
| 270 | err_out_unlock: | ||
| 271 | spin_unlock_bh(&queue_lock); | ||
| 272 | return status; | ||
| 273 | } | ||
| 274 | |||
| 275 | static int | ||
| 276 | ipq_mangle_ipv4(ipq_verdict_msg_t *v, struct nf_queue_entry *e) | ||
| 277 | { | ||
| 278 | int diff; | ||
| 279 | struct iphdr *user_iph = (struct iphdr *)v->payload; | ||
| 280 | struct sk_buff *nskb; | ||
| 281 | |||
| 282 | if (v->data_len < sizeof(*user_iph)) | ||
| 283 | return 0; | ||
| 284 | diff = v->data_len - e->skb->len; | ||
| 285 | if (diff < 0) { | ||
| 286 | if (pskb_trim(e->skb, v->data_len)) | ||
| 287 | return -ENOMEM; | ||
| 288 | } else if (diff > 0) { | ||
| 289 | if (v->data_len > 0xFFFF) | ||
| 290 | return -EINVAL; | ||
| 291 | if (diff > skb_tailroom(e->skb)) { | ||
| 292 | nskb = skb_copy_expand(e->skb, skb_headroom(e->skb), | ||
| 293 | diff, GFP_ATOMIC); | ||
| 294 | if (!nskb) { | ||
| 295 | printk(KERN_WARNING "ip_queue: error " | ||
| 296 | "in mangle, dropping packet\n"); | ||
| 297 | return -ENOMEM; | ||
| 298 | } | ||
| 299 | kfree_skb(e->skb); | ||
| 300 | e->skb = nskb; | ||
| 301 | } | ||
| 302 | skb_put(e->skb, diff); | ||
| 303 | } | ||
| 304 | if (!skb_make_writable(e->skb, v->data_len)) | ||
| 305 | return -ENOMEM; | ||
| 306 | skb_copy_to_linear_data(e->skb, v->payload, v->data_len); | ||
| 307 | e->skb->ip_summed = CHECKSUM_NONE; | ||
| 308 | |||
| 309 | return 0; | ||
| 310 | } | ||
| 311 | |||
| 312 | static int | ||
| 313 | ipq_set_verdict(struct ipq_verdict_msg *vmsg, unsigned int len) | ||
| 314 | { | ||
| 315 | struct nf_queue_entry *entry; | ||
| 316 | |||
| 317 | if (vmsg->value > NF_MAX_VERDICT || vmsg->value == NF_STOLEN) | ||
| 318 | return -EINVAL; | ||
| 319 | |||
| 320 | entry = ipq_find_dequeue_entry(vmsg->id); | ||
| 321 | if (entry == NULL) | ||
| 322 | return -ENOENT; | ||
| 323 | else { | ||
| 324 | int verdict = vmsg->value; | ||
| 325 | |||
| 326 | if (vmsg->data_len && vmsg->data_len == len) | ||
| 327 | if (ipq_mangle_ipv4(vmsg, entry) < 0) | ||
| 328 | verdict = NF_DROP; | ||
| 329 | |||
| 330 | nf_reinject(entry, verdict); | ||
| 331 | return 0; | ||
| 332 | } | ||
| 333 | } | ||
| 334 | |||
| 335 | static int | ||
| 336 | ipq_set_mode(unsigned char mode, unsigned int range) | ||
| 337 | { | ||
| 338 | int status; | ||
| 339 | |||
| 340 | spin_lock_bh(&queue_lock); | ||
| 341 | status = __ipq_set_mode(mode, range); | ||
| 342 | spin_unlock_bh(&queue_lock); | ||
| 343 | return status; | ||
| 344 | } | ||
| 345 | |||
| 346 | static int | ||
| 347 | ipq_receive_peer(struct ipq_peer_msg *pmsg, | ||
| 348 | unsigned char type, unsigned int len) | ||
| 349 | { | ||
| 350 | int status = 0; | ||
| 351 | |||
| 352 | if (len < sizeof(*pmsg)) | ||
| 353 | return -EINVAL; | ||
| 354 | |||
| 355 | switch (type) { | ||
| 356 | case IPQM_MODE: | ||
| 357 | status = ipq_set_mode(pmsg->msg.mode.value, | ||
| 358 | pmsg->msg.mode.range); | ||
| 359 | break; | ||
| 360 | |||
| 361 | case IPQM_VERDICT: | ||
| 362 | status = ipq_set_verdict(&pmsg->msg.verdict, | ||
| 363 | len - sizeof(*pmsg)); | ||
| 364 | break; | ||
| 365 | default: | ||
| 366 | status = -EINVAL; | ||
| 367 | } | ||
| 368 | return status; | ||
| 369 | } | ||
| 370 | |||
| 371 | static int | ||
| 372 | dev_cmp(struct nf_queue_entry *entry, unsigned long ifindex) | ||
| 373 | { | ||
| 374 | if (entry->indev) | ||
| 375 | if (entry->indev->ifindex == ifindex) | ||
| 376 | return 1; | ||
| 377 | if (entry->outdev) | ||
| 378 | if (entry->outdev->ifindex == ifindex) | ||
| 379 | return 1; | ||
| 380 | #ifdef CONFIG_BRIDGE_NETFILTER | ||
| 381 | if (entry->skb->nf_bridge) { | ||
| 382 | if (entry->skb->nf_bridge->physindev && | ||
| 383 | entry->skb->nf_bridge->physindev->ifindex == ifindex) | ||
| 384 | return 1; | ||
| 385 | if (entry->skb->nf_bridge->physoutdev && | ||
| 386 | entry->skb->nf_bridge->physoutdev->ifindex == ifindex) | ||
| 387 | return 1; | ||
| 388 | } | ||
| 389 | #endif | ||
| 390 | return 0; | ||
| 391 | } | ||
| 392 | |||
| 393 | static void | ||
| 394 | ipq_dev_drop(int ifindex) | ||
| 395 | { | ||
| 396 | ipq_flush(dev_cmp, ifindex); | ||
| 397 | } | ||
| 398 | |||
| 399 | #define RCV_SKB_FAIL(err) do { netlink_ack(skb, nlh, (err)); return; } while (0) | ||
| 400 | |||
| 401 | static inline void | ||
| 402 | __ipq_rcv_skb(struct sk_buff *skb) | ||
| 403 | { | ||
| 404 | int status, type, pid, flags; | ||
| 405 | unsigned int nlmsglen, skblen; | ||
| 406 | struct nlmsghdr *nlh; | ||
| 407 | |||
| 408 | skblen = skb->len; | ||
| 409 | if (skblen < sizeof(*nlh)) | ||
| 410 | return; | ||
| 411 | |||
| 412 | nlh = nlmsg_hdr(skb); | ||
| 413 | nlmsglen = nlh->nlmsg_len; | ||
| 414 | if (nlmsglen < sizeof(*nlh) || skblen < nlmsglen) | ||
| 415 | return; | ||
| 416 | |||
| 417 | pid = nlh->nlmsg_pid; | ||
| 418 | flags = nlh->nlmsg_flags; | ||
| 419 | |||
| 420 | if(pid <= 0 || !(flags & NLM_F_REQUEST) || flags & NLM_F_MULTI) | ||
| 421 | RCV_SKB_FAIL(-EINVAL); | ||
| 422 | |||
| 423 | if (flags & MSG_TRUNC) | ||
| 424 | RCV_SKB_FAIL(-ECOMM); | ||
| 425 | |||
| 426 | type = nlh->nlmsg_type; | ||
| 427 | if (type < NLMSG_NOOP || type >= IPQM_MAX) | ||
| 428 | RCV_SKB_FAIL(-EINVAL); | ||
| 429 | |||
| 430 | if (type <= IPQM_BASE) | ||
| 431 | return; | ||
| 432 | |||
| 433 | if (security_netlink_recv(skb, CAP_NET_ADMIN)) | ||
| 434 | RCV_SKB_FAIL(-EPERM); | ||
| 435 | |||
| 436 | spin_lock_bh(&queue_lock); | ||
| 437 | |||
| 438 | if (peer_pid) { | ||
| 439 | if (peer_pid != pid) { | ||
| 440 | spin_unlock_bh(&queue_lock); | ||
| 441 | RCV_SKB_FAIL(-EBUSY); | ||
| 442 | } | ||
| 443 | } else { | ||
| 444 | net_enable_timestamp(); | ||
| 445 | peer_pid = pid; | ||
| 446 | } | ||
| 447 | |||
| 448 | spin_unlock_bh(&queue_lock); | ||
| 449 | |||
| 450 | status = ipq_receive_peer(NLMSG_DATA(nlh), type, | ||
| 451 | nlmsglen - NLMSG_LENGTH(0)); | ||
| 452 | if (status < 0) | ||
| 453 | RCV_SKB_FAIL(status); | ||
| 454 | |||
| 455 | if (flags & NLM_F_ACK) | ||
| 456 | netlink_ack(skb, nlh, 0); | ||
| 457 | } | ||
| 458 | |||
| 459 | static void | ||
| 460 | ipq_rcv_skb(struct sk_buff *skb) | ||
| 461 | { | ||
| 462 | mutex_lock(&ipqnl_mutex); | ||
| 463 | __ipq_rcv_skb(skb); | ||
| 464 | mutex_unlock(&ipqnl_mutex); | ||
| 465 | } | ||
| 466 | |||
| 467 | static int | ||
| 468 | ipq_rcv_dev_event(struct notifier_block *this, | ||
| 469 | unsigned long event, void *ptr) | ||
| 470 | { | ||
| 471 | struct net_device *dev = ptr; | ||
| 472 | |||
| 473 | if (!net_eq(dev_net(dev), &init_net)) | ||
| 474 | return NOTIFY_DONE; | ||
| 475 | |||
| 476 | /* Drop any packets associated with the downed device */ | ||
| 477 | if (event == NETDEV_DOWN) | ||
| 478 | ipq_dev_drop(dev->ifindex); | ||
| 479 | return NOTIFY_DONE; | ||
| 480 | } | ||
| 481 | |||
| 482 | static struct notifier_block ipq_dev_notifier = { | ||
| 483 | .notifier_call = ipq_rcv_dev_event, | ||
| 484 | }; | ||
| 485 | |||
| 486 | static int | ||
| 487 | ipq_rcv_nl_event(struct notifier_block *this, | ||
| 488 | unsigned long event, void *ptr) | ||
| 489 | { | ||
| 490 | struct netlink_notify *n = ptr; | ||
| 491 | |||
| 492 | if (event == NETLINK_URELEASE && n->protocol == NETLINK_FIREWALL) { | ||
| 493 | spin_lock_bh(&queue_lock); | ||
| 494 | if ((net_eq(n->net, &init_net)) && (n->pid == peer_pid)) | ||
| 495 | __ipq_reset(); | ||
| 496 | spin_unlock_bh(&queue_lock); | ||
| 497 | } | ||
| 498 | return NOTIFY_DONE; | ||
| 499 | } | ||
| 500 | |||
| 501 | static struct notifier_block ipq_nl_notifier = { | ||
| 502 | .notifier_call = ipq_rcv_nl_event, | ||
| 503 | }; | ||
| 504 | |||
| 505 | #ifdef CONFIG_SYSCTL | ||
| 506 | static struct ctl_table_header *ipq_sysctl_header; | ||
| 507 | |||
| 508 | static ctl_table ipq_table[] = { | ||
| 509 | { | ||
| 510 | .procname = NET_IPQ_QMAX_NAME, | ||
| 511 | .data = &queue_maxlen, | ||
| 512 | .maxlen = sizeof(queue_maxlen), | ||
| 513 | .mode = 0644, | ||
| 514 | .proc_handler = proc_dointvec | ||
| 515 | }, | ||
| 516 | { } | ||
| 517 | }; | ||
| 518 | #endif | ||
| 519 | |||
| 520 | #ifdef CONFIG_PROC_FS | ||
| 521 | static int ip_queue_show(struct seq_file *m, void *v) | ||
| 522 | { | ||
| 523 | spin_lock_bh(&queue_lock); | ||
| 524 | |||
| 525 | seq_printf(m, | ||
| 526 | "Peer PID : %d\n" | ||
| 527 | "Copy mode : %hu\n" | ||
| 528 | "Copy range : %u\n" | ||
| 529 | "Queue length : %u\n" | ||
| 530 | "Queue max. length : %u\n" | ||
| 531 | "Queue dropped : %u\n" | ||
| 532 | "Netlink dropped : %u\n", | ||
| 533 | peer_pid, | ||
| 534 | copy_mode, | ||
| 535 | copy_range, | ||
| 536 | queue_total, | ||
| 537 | queue_maxlen, | ||
| 538 | queue_dropped, | ||
| 539 | queue_user_dropped); | ||
| 540 | |||
| 541 | spin_unlock_bh(&queue_lock); | ||
| 542 | return 0; | ||
| 543 | } | ||
| 544 | |||
| 545 | static int ip_queue_open(struct inode *inode, struct file *file) | ||
| 546 | { | ||
| 547 | return single_open(file, ip_queue_show, NULL); | ||
| 548 | } | ||
| 549 | |||
| 550 | static const struct file_operations ip_queue_proc_fops = { | ||
| 551 | .open = ip_queue_open, | ||
| 552 | .read = seq_read, | ||
| 553 | .llseek = seq_lseek, | ||
| 554 | .release = single_release, | ||
| 555 | .owner = THIS_MODULE, | ||
| 556 | }; | ||
| 557 | #endif | ||
| 558 | |||
| 559 | static const struct nf_queue_handler nfqh = { | ||
| 560 | .name = "ip_queue", | ||
| 561 | .outfn = &ipq_enqueue_packet, | ||
| 562 | }; | ||
| 563 | |||
| 564 | static int __init ip_queue_init(void) | ||
| 565 | { | ||
| 566 | int status = -ENOMEM; | ||
| 567 | struct proc_dir_entry *proc __maybe_unused; | ||
| 568 | |||
| 569 | netlink_register_notifier(&ipq_nl_notifier); | ||
| 570 | ipqnl = netlink_kernel_create(&init_net, NETLINK_FIREWALL, 0, | ||
| 571 | ipq_rcv_skb, NULL, THIS_MODULE); | ||
| 572 | if (ipqnl == NULL) { | ||
| 573 | printk(KERN_ERR "ip_queue: failed to create netlink socket\n"); | ||
| 574 | goto cleanup_netlink_notifier; | ||
| 575 | } | ||
| 576 | |||
| 577 | #ifdef CONFIG_PROC_FS | ||
| 578 | proc = proc_create(IPQ_PROC_FS_NAME, 0, init_net.proc_net, | ||
| 579 | &ip_queue_proc_fops); | ||
| 580 | if (!proc) { | ||
| 581 | printk(KERN_ERR "ip_queue: failed to create proc entry\n"); | ||
| 582 | goto cleanup_ipqnl; | ||
| 583 | } | ||
| 584 | #endif | ||
| 585 | register_netdevice_notifier(&ipq_dev_notifier); | ||
| 586 | #ifdef CONFIG_SYSCTL | ||
| 587 | ipq_sysctl_header = register_sysctl_paths(net_ipv4_ctl_path, ipq_table); | ||
| 588 | #endif | ||
| 589 | status = nf_register_queue_handler(NFPROTO_IPV4, &nfqh); | ||
| 590 | if (status < 0) { | ||
| 591 | printk(KERN_ERR "ip_queue: failed to register queue handler\n"); | ||
| 592 | goto cleanup_sysctl; | ||
| 593 | } | ||
| 594 | return status; | ||
| 595 | |||
| 596 | cleanup_sysctl: | ||
| 597 | #ifdef CONFIG_SYSCTL | ||
| 598 | unregister_sysctl_table(ipq_sysctl_header); | ||
| 599 | #endif | ||
| 600 | unregister_netdevice_notifier(&ipq_dev_notifier); | ||
| 601 | proc_net_remove(&init_net, IPQ_PROC_FS_NAME); | ||
| 602 | cleanup_ipqnl: __maybe_unused | ||
| 603 | netlink_kernel_release(ipqnl); | ||
| 604 | mutex_lock(&ipqnl_mutex); | ||
| 605 | mutex_unlock(&ipqnl_mutex); | ||
| 606 | |||
| 607 | cleanup_netlink_notifier: | ||
| 608 | netlink_unregister_notifier(&ipq_nl_notifier); | ||
| 609 | return status; | ||
| 610 | } | ||
| 611 | |||
| 612 | static void __exit ip_queue_fini(void) | ||
| 613 | { | ||
| 614 | nf_unregister_queue_handlers(&nfqh); | ||
| 615 | |||
| 616 | ipq_flush(NULL, 0); | ||
| 617 | |||
| 618 | #ifdef CONFIG_SYSCTL | ||
| 619 | unregister_sysctl_table(ipq_sysctl_header); | ||
| 620 | #endif | ||
| 621 | unregister_netdevice_notifier(&ipq_dev_notifier); | ||
| 622 | proc_net_remove(&init_net, IPQ_PROC_FS_NAME); | ||
| 623 | |||
| 624 | netlink_kernel_release(ipqnl); | ||
| 625 | mutex_lock(&ipqnl_mutex); | ||
| 626 | mutex_unlock(&ipqnl_mutex); | ||
| 627 | |||
| 628 | netlink_unregister_notifier(&ipq_nl_notifier); | ||
| 629 | } | ||
| 630 | |||
| 631 | MODULE_DESCRIPTION("IPv4 packet queue handler"); | ||
| 632 | MODULE_AUTHOR("James Morris <jmorris@intercode.com.au>"); | ||
| 633 | MODULE_LICENSE("GPL"); | ||
| 634 | MODULE_ALIAS_NET_PF_PROTO(PF_NETLINK, NETLINK_FIREWALL); | ||
| 635 | |||
| 636 | module_init(ip_queue_init); | ||
| 637 | module_exit(ip_queue_fini); | ||
diff --git a/net/ipv4/netfilter/ipt_LOG.c b/net/ipv4/netfilter/ipt_LOG.c new file mode 100644 index 00000000000..d76d6c9ed94 --- /dev/null +++ b/net/ipv4/netfilter/ipt_LOG.c | |||
| @@ -0,0 +1,516 @@ | |||
| 1 | /* | ||
| 2 | * This is a module which is used for logging packets. | ||
| 3 | */ | ||
| 4 | |||
| 5 | /* (C) 1999-2001 Paul `Rusty' Russell | ||
| 6 | * (C) 2002-2004 Netfilter Core Team <coreteam@netfilter.org> | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or modify | ||
| 9 | * it under the terms of the GNU General Public License version 2 as | ||
| 10 | * published by the Free Software Foundation. | ||
| 11 | */ | ||
| 12 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
| 13 | #include <linux/module.h> | ||
| 14 | #include <linux/spinlock.h> | ||
| 15 | #include <linux/skbuff.h> | ||
| 16 | #include <linux/if_arp.h> | ||
| 17 | #include <linux/ip.h> | ||
| 18 | #include <net/icmp.h> | ||
| 19 | #include <net/udp.h> | ||
| 20 | #include <net/tcp.h> | ||
| 21 | #include <net/route.h> | ||
| 22 | |||
| 23 | #include <linux/netfilter.h> | ||
| 24 | #include <linux/netfilter/x_tables.h> | ||
| 25 | #include <linux/netfilter_ipv4/ipt_LOG.h> | ||
| 26 | #include <net/netfilter/nf_log.h> | ||
| 27 | #include <net/netfilter/xt_log.h> | ||
| 28 | |||
| 29 | MODULE_LICENSE("GPL"); | ||
| 30 | MODULE_AUTHOR("Netfilter Core Team <coreteam@netfilter.org>"); | ||
| 31 | MODULE_DESCRIPTION("Xtables: IPv4 packet logging to syslog"); | ||
| 32 | |||
| 33 | /* One level of recursion won't kill us */ | ||
| 34 | static void dump_packet(struct sbuff *m, | ||
| 35 | const struct nf_loginfo *info, | ||
| 36 | const struct sk_buff *skb, | ||
| 37 | unsigned int iphoff) | ||
| 38 | { | ||
| 39 | struct iphdr _iph; | ||
| 40 | const struct iphdr *ih; | ||
| 41 | unsigned int logflags; | ||
| 42 | |||
| 43 | if (info->type == NF_LOG_TYPE_LOG) | ||
| 44 | logflags = info->u.log.logflags; | ||
| 45 | else | ||
| 46 | logflags = NF_LOG_MASK; | ||
| 47 | |||
| 48 | ih = skb_header_pointer(skb, iphoff, sizeof(_iph), &_iph); | ||
| 49 | if (ih == NULL) { | ||
| 50 | sb_add(m, "TRUNCATED"); | ||
| 51 | return; | ||
| 52 | } | ||
| 53 | |||
| 54 | /* Important fields: | ||
| 55 | * TOS, len, DF/MF, fragment offset, TTL, src, dst, options. */ | ||
| 56 | /* Max length: 40 "SRC=255.255.255.255 DST=255.255.255.255 " */ | ||
| 57 | sb_add(m, "SRC=%pI4 DST=%pI4 ", | ||
| 58 | &ih->saddr, &ih->daddr); | ||
| 59 | |||
| 60 | /* Max length: 46 "LEN=65535 TOS=0xFF PREC=0xFF TTL=255 ID=65535 " */ | ||
| 61 | sb_add(m, "LEN=%u TOS=0x%02X PREC=0x%02X TTL=%u ID=%u ", | ||
| 62 | ntohs(ih->tot_len), ih->tos & IPTOS_TOS_MASK, | ||
| 63 | ih->tos & IPTOS_PREC_MASK, ih->ttl, ntohs(ih->id)); | ||
| 64 | |||
| 65 | /* Max length: 6 "CE DF MF " */ | ||
| 66 | if (ntohs(ih->frag_off) & IP_CE) | ||
| 67 | sb_add(m, "CE "); | ||
| 68 | if (ntohs(ih->frag_off) & IP_DF) | ||
| 69 | sb_add(m, "DF "); | ||
| 70 | if (ntohs(ih->frag_off) & IP_MF) | ||
| 71 | sb_add(m, "MF "); | ||
| 72 | |||
| 73 | /* Max length: 11 "FRAG:65535 " */ | ||
| 74 | if (ntohs(ih->frag_off) & IP_OFFSET) | ||
| 75 | sb_add(m, "FRAG:%u ", ntohs(ih->frag_off) & IP_OFFSET); | ||
| 76 | |||
| 77 | if ((logflags & IPT_LOG_IPOPT) && | ||
| 78 | ih->ihl * 4 > sizeof(struct iphdr)) { | ||
| 79 | const unsigned char *op; | ||
| 80 | unsigned char _opt[4 * 15 - sizeof(struct iphdr)]; | ||
| 81 | unsigned int i, optsize; | ||
| 82 | |||
| 83 | optsize = ih->ihl * 4 - sizeof(struct iphdr); | ||
| 84 | op = skb_header_pointer(skb, iphoff+sizeof(_iph), | ||
| 85 | optsize, _opt); | ||
| 86 | if (op == NULL) { | ||
| 87 | sb_add(m, "TRUNCATED"); | ||
| 88 | return; | ||
| 89 | } | ||
| 90 | |||
| 91 | /* Max length: 127 "OPT (" 15*4*2chars ") " */ | ||
| 92 | sb_add(m, "OPT ("); | ||
| 93 | for (i = 0; i < optsize; i++) | ||
| 94 | sb_add(m, "%02X", op[i]); | ||
| 95 | sb_add(m, ") "); | ||
| 96 | } | ||
| 97 | |||
| 98 | switch (ih->protocol) { | ||
| 99 | case IPPROTO_TCP: { | ||
| 100 | struct tcphdr _tcph; | ||
| 101 | const struct tcphdr *th; | ||
| 102 | |||
| 103 | /* Max length: 10 "PROTO=TCP " */ | ||
| 104 | sb_add(m, "PROTO=TCP "); | ||
| 105 | |||
| 106 | if (ntohs(ih->frag_off) & IP_OFFSET) | ||
| 107 | break; | ||
| 108 | |||
| 109 | /* Max length: 25 "INCOMPLETE [65535 bytes] " */ | ||
| 110 | th = skb_header_pointer(skb, iphoff + ih->ihl * 4, | ||
| 111 | sizeof(_tcph), &_tcph); | ||
| 112 | if (th == NULL) { | ||
| 113 | sb_add(m, "INCOMPLETE [%u bytes] ", | ||
| 114 | skb->len - iphoff - ih->ihl*4); | ||
| 115 | break; | ||
| 116 | } | ||
| 117 | |||
| 118 | /* Max length: 20 "SPT=65535 DPT=65535 " */ | ||
| 119 | sb_add(m, "SPT=%u DPT=%u ", | ||
| 120 | ntohs(th->source), ntohs(th->dest)); | ||
| 121 | /* Max length: 30 "SEQ=4294967295 ACK=4294967295 " */ | ||
| 122 | if (logflags & IPT_LOG_TCPSEQ) | ||
| 123 | sb_add(m, "SEQ=%u ACK=%u ", | ||
| 124 | ntohl(th->seq), ntohl(th->ack_seq)); | ||
| 125 | /* Max length: 13 "WINDOW=65535 " */ | ||
| 126 | sb_add(m, "WINDOW=%u ", ntohs(th->window)); | ||
| 127 | /* Max length: 9 "RES=0x3F " */ | ||
| 128 | sb_add(m, "RES=0x%02x ", (u8)(ntohl(tcp_flag_word(th) & TCP_RESERVED_BITS) >> 22)); | ||
| 129 | /* Max length: 32 "CWR ECE URG ACK PSH RST SYN FIN " */ | ||
| 130 | if (th->cwr) | ||
| 131 | sb_add(m, "CWR "); | ||
| 132 | if (th->ece) | ||
| 133 | sb_add(m, "ECE "); | ||
| 134 | if (th->urg) | ||
| 135 | sb_add(m, "URG "); | ||
| 136 | if (th->ack) | ||
| 137 | sb_add(m, "ACK "); | ||
| 138 | if (th->psh) | ||
| 139 | sb_add(m, "PSH "); | ||
| 140 | if (th->rst) | ||
| 141 | sb_add(m, "RST "); | ||
| 142 | if (th->syn) | ||
| 143 | sb_add(m, "SYN "); | ||
| 144 | if (th->fin) | ||
| 145 | sb_add(m, "FIN "); | ||
| 146 | /* Max length: 11 "URGP=65535 " */ | ||
| 147 | sb_add(m, "URGP=%u ", ntohs(th->urg_ptr)); | ||
| 148 | |||
| 149 | if ((logflags & IPT_LOG_TCPOPT) && | ||
| 150 | th->doff * 4 > sizeof(struct tcphdr)) { | ||
| 151 | unsigned char _opt[4 * 15 - sizeof(struct tcphdr)]; | ||
| 152 | const unsigned char *op; | ||
| 153 | unsigned int i, optsize; | ||
| 154 | |||
| 155 | optsize = th->doff * 4 - sizeof(struct tcphdr); | ||
| 156 | op = skb_header_pointer(skb, | ||
| 157 | iphoff+ih->ihl*4+sizeof(_tcph), | ||
| 158 | optsize, _opt); | ||
| 159 | if (op == NULL) { | ||
| 160 | sb_add(m, "TRUNCATED"); | ||
| 161 | return; | ||
| 162 | } | ||
| 163 | |||
| 164 | /* Max length: 127 "OPT (" 15*4*2chars ") " */ | ||
| 165 | sb_add(m, "OPT ("); | ||
| 166 | for (i = 0; i < optsize; i++) | ||
| 167 | sb_add(m, "%02X", op[i]); | ||
| 168 | sb_add(m, ") "); | ||
| 169 | } | ||
| 170 | break; | ||
| 171 | } | ||
| 172 | case IPPROTO_UDP: | ||
| 173 | case IPPROTO_UDPLITE: { | ||
| 174 | struct udphdr _udph; | ||
| 175 | const struct udphdr *uh; | ||
| 176 | |||
| 177 | if (ih->protocol == IPPROTO_UDP) | ||
| 178 | /* Max length: 10 "PROTO=UDP " */ | ||
| 179 | sb_add(m, "PROTO=UDP " ); | ||
| 180 | else /* Max length: 14 "PROTO=UDPLITE " */ | ||
| 181 | sb_add(m, "PROTO=UDPLITE "); | ||
| 182 | |||
| 183 | if (ntohs(ih->frag_off) & IP_OFFSET) | ||
| 184 | break; | ||
| 185 | |||
| 186 | /* Max length: 25 "INCOMPLETE [65535 bytes] " */ | ||
| 187 | uh = skb_header_pointer(skb, iphoff+ih->ihl*4, | ||
| 188 | sizeof(_udph), &_udph); | ||
| 189 | if (uh == NULL) { | ||
| 190 | sb_add(m, "INCOMPLETE [%u bytes] ", | ||
| 191 | skb->len - iphoff - ih->ihl*4); | ||
| 192 | break; | ||
| 193 | } | ||
| 194 | |||
| 195 | /* Max length: 20 "SPT=65535 DPT=65535 " */ | ||
| 196 | sb_add(m, "SPT=%u DPT=%u LEN=%u ", | ||
| 197 | ntohs(uh->source), ntohs(uh->dest), | ||
| 198 | ntohs(uh->len)); | ||
| 199 | break; | ||
| 200 | } | ||
| 201 | case IPPROTO_ICMP: { | ||
| 202 | struct icmphdr _icmph; | ||
| 203 | const struct icmphdr *ich; | ||
| 204 | static const size_t required_len[NR_ICMP_TYPES+1] | ||
| 205 | = { [ICMP_ECHOREPLY] = 4, | ||
| 206 | [ICMP_DEST_UNREACH] | ||
| 207 | = 8 + sizeof(struct iphdr), | ||
| 208 | [ICMP_SOURCE_QUENCH] | ||
| 209 | = 8 + sizeof(struct iphdr), | ||
| 210 | [ICMP_REDIRECT] | ||
| 211 | = 8 + sizeof(struct iphdr), | ||
| 212 | [ICMP_ECHO] = 4, | ||
| 213 | [ICMP_TIME_EXCEEDED] | ||
| 214 | = 8 + sizeof(struct iphdr), | ||
| 215 | [ICMP_PARAMETERPROB] | ||
| 216 | = 8 + sizeof(struct iphdr), | ||
| 217 | [ICMP_TIMESTAMP] = 20, | ||
| 218 | [ICMP_TIMESTAMPREPLY] = 20, | ||
| 219 | [ICMP_ADDRESS] = 12, | ||
| 220 | [ICMP_ADDRESSREPLY] = 12 }; | ||
| 221 | |||
| 222 | /* Max length: 11 "PROTO=ICMP " */ | ||
| 223 | sb_add(m, "PROTO=ICMP "); | ||
| 224 | |||
| 225 | if (ntohs(ih->frag_off) & IP_OFFSET) | ||
| 226 | break; | ||
| 227 | |||
| 228 | /* Max length: 25 "INCOMPLETE [65535 bytes] " */ | ||
| 229 | ich = skb_header_pointer(skb, iphoff + ih->ihl * 4, | ||
| 230 | sizeof(_icmph), &_icmph); | ||
| 231 | if (ich == NULL) { | ||
| 232 | sb_add(m, "INCOMPLETE [%u bytes] ", | ||
| 233 | skb->len - iphoff - ih->ihl*4); | ||
| 234 | break; | ||
| 235 | } | ||
| 236 | |||
| 237 | /* Max length: 18 "TYPE=255 CODE=255 " */ | ||
| 238 | sb_add(m, "TYPE=%u CODE=%u ", ich->type, ich->code); | ||
| 239 | |||
| 240 | /* Max length: 25 "INCOMPLETE [65535 bytes] " */ | ||
| 241 | if (ich->type <= NR_ICMP_TYPES && | ||
| 242 | required_len[ich->type] && | ||
| 243 | skb->len-iphoff-ih->ihl*4 < required_len[ich->type]) { | ||
| 244 | sb_add(m, "INCOMPLETE [%u bytes] ", | ||
| 245 | skb->len - iphoff - ih->ihl*4); | ||
| 246 | break; | ||
| 247 | } | ||
| 248 | |||
| 249 | switch (ich->type) { | ||
| 250 | case ICMP_ECHOREPLY: | ||
| 251 | case ICMP_ECHO: | ||
| 252 | /* Max length: 19 "ID=65535 SEQ=65535 " */ | ||
| 253 | sb_add(m, "ID=%u SEQ=%u ", | ||
| 254 | ntohs(ich->un.echo.id), | ||
| 255 | ntohs(ich->un.echo.sequence)); | ||
| 256 | break; | ||
| 257 | |||
| 258 | case ICMP_PARAMETERPROB: | ||
| 259 | /* Max length: 14 "PARAMETER=255 " */ | ||
| 260 | sb_add(m, "PARAMETER=%u ", | ||
| 261 | ntohl(ich->un.gateway) >> 24); | ||
| 262 | break; | ||
| 263 | case ICMP_REDIRECT: | ||
| 264 | /* Max length: 24 "GATEWAY=255.255.255.255 " */ | ||
| 265 | sb_add(m, "GATEWAY=%pI4 ", &ich->un.gateway); | ||
| 266 | /* Fall through */ | ||
| 267 | case ICMP_DEST_UNREACH: | ||
| 268 | case ICMP_SOURCE_QUENCH: | ||
| 269 | case ICMP_TIME_EXCEEDED: | ||
| 270 | /* Max length: 3+maxlen */ | ||
| 271 | if (!iphoff) { /* Only recurse once. */ | ||
| 272 | sb_add(m, "["); | ||
| 273 | dump_packet(m, info, skb, | ||
| 274 | iphoff + ih->ihl*4+sizeof(_icmph)); | ||
| 275 | sb_add(m, "] "); | ||
| 276 | } | ||
| 277 | |||
| 278 | /* Max length: 10 "MTU=65535 " */ | ||
| 279 | if (ich->type == ICMP_DEST_UNREACH && | ||
| 280 | ich->code == ICMP_FRAG_NEEDED) | ||
| 281 | sb_add(m, "MTU=%u ", ntohs(ich->un.frag.mtu)); | ||
| 282 | } | ||
| 283 | break; | ||
| 284 | } | ||
| 285 | /* Max Length */ | ||
| 286 | case IPPROTO_AH: { | ||
| 287 | struct ip_auth_hdr _ahdr; | ||
| 288 | const struct ip_auth_hdr *ah; | ||
| 289 | |||
| 290 | if (ntohs(ih->frag_off) & IP_OFFSET) | ||
| 291 | break; | ||
| 292 | |||
| 293 | /* Max length: 9 "PROTO=AH " */ | ||
| 294 | sb_add(m, "PROTO=AH "); | ||
| 295 | |||
| 296 | /* Max length: 25 "INCOMPLETE [65535 bytes] " */ | ||
| 297 | ah = skb_header_pointer(skb, iphoff+ih->ihl*4, | ||
| 298 | sizeof(_ahdr), &_ahdr); | ||
| 299 | if (ah == NULL) { | ||
| 300 | sb_add(m, "INCOMPLETE [%u bytes] ", | ||
| 301 | skb->len - iphoff - ih->ihl*4); | ||
| 302 | break; | ||
| 303 | } | ||
| 304 | |||
| 305 | /* Length: 15 "SPI=0xF1234567 " */ | ||
| 306 | sb_add(m, "SPI=0x%x ", ntohl(ah->spi)); | ||
| 307 | break; | ||
| 308 | } | ||
| 309 | case IPPROTO_ESP: { | ||
| 310 | struct ip_esp_hdr _esph; | ||
| 311 | const struct ip_esp_hdr *eh; | ||
| 312 | |||
| 313 | /* Max length: 10 "PROTO=ESP " */ | ||
| 314 | sb_add(m, "PROTO=ESP "); | ||
| 315 | |||
| 316 | if (ntohs(ih->frag_off) & IP_OFFSET) | ||
| 317 | break; | ||
| 318 | |||
| 319 | /* Max length: 25 "INCOMPLETE [65535 bytes] " */ | ||
| 320 | eh = skb_header_pointer(skb, iphoff+ih->ihl*4, | ||
| 321 | sizeof(_esph), &_esph); | ||
| 322 | if (eh == NULL) { | ||
| 323 | sb_add(m, "INCOMPLETE [%u bytes] ", | ||
| 324 | skb->len - iphoff - ih->ihl*4); | ||
| 325 | break; | ||
| 326 | } | ||
| 327 | |||
| 328 | /* Length: 15 "SPI=0xF1234567 " */ | ||
| 329 | sb_add(m, "SPI=0x%x ", ntohl(eh->spi)); | ||
| 330 | break; | ||
| 331 | } | ||
| 332 | /* Max length: 10 "PROTO 255 " */ | ||
| 333 | default: | ||
| 334 | sb_add(m, "PROTO=%u ", ih->protocol); | ||
| 335 | } | ||
| 336 | |||
| 337 | /* Max length: 15 "UID=4294967295 " */ | ||
| 338 | if ((logflags & IPT_LOG_UID) && !iphoff && skb->sk) { | ||
| 339 | read_lock_bh(&skb->sk->sk_callback_lock); | ||
| 340 | if (skb->sk->sk_socket && skb->sk->sk_socket->file) | ||
| 341 | sb_add(m, "UID=%u GID=%u ", | ||
| 342 | skb->sk->sk_socket->file->f_cred->fsuid, | ||
| 343 | skb->sk->sk_socket->file->f_cred->fsgid); | ||
| 344 | read_unlock_bh(&skb->sk->sk_callback_lock); | ||
| 345 | } | ||
| 346 | |||
| 347 | /* Max length: 16 "MARK=0xFFFFFFFF " */ | ||
| 348 | if (!iphoff && skb->mark) | ||
| 349 | sb_add(m, "MARK=0x%x ", skb->mark); | ||
| 350 | |||
| 351 | /* Proto Max log string length */ | ||
| 352 | /* IP: 40+46+6+11+127 = 230 */ | ||
| 353 | /* TCP: 10+max(25,20+30+13+9+32+11+127) = 252 */ | ||
| 354 | /* UDP: 10+max(25,20) = 35 */ | ||
| 355 | /* UDPLITE: 14+max(25,20) = 39 */ | ||
| 356 | /* ICMP: 11+max(25, 18+25+max(19,14,24+3+n+10,3+n+10)) = 91+n */ | ||
| 357 | /* ESP: 10+max(25)+15 = 50 */ | ||
| 358 | /* AH: 9+max(25)+15 = 49 */ | ||
| 359 | /* unknown: 10 */ | ||
| 360 | |||
| 361 | /* (ICMP allows recursion one level deep) */ | ||
| 362 | /* maxlen = IP + ICMP + IP + max(TCP,UDP,ICMP,unknown) */ | ||
| 363 | /* maxlen = 230+ 91 + 230 + 252 = 803 */ | ||
| 364 | } | ||
| 365 | |||
| 366 | static void dump_mac_header(struct sbuff *m, | ||
| 367 | const struct nf_loginfo *info, | ||
| 368 | const struct sk_buff *skb) | ||
| 369 | { | ||
| 370 | struct net_device *dev = skb->dev; | ||
| 371 | unsigned int logflags = 0; | ||
| 372 | |||
| 373 | if (info->type == NF_LOG_TYPE_LOG) | ||
| 374 | logflags = info->u.log.logflags; | ||
| 375 | |||
| 376 | if (!(logflags & IPT_LOG_MACDECODE)) | ||
| 377 | goto fallback; | ||
| 378 | |||
| 379 | switch (dev->type) { | ||
| 380 | case ARPHRD_ETHER: | ||
| 381 | sb_add(m, "MACSRC=%pM MACDST=%pM MACPROTO=%04x ", | ||
| 382 | eth_hdr(skb)->h_source, eth_hdr(skb)->h_dest, | ||
| 383 | ntohs(eth_hdr(skb)->h_proto)); | ||
| 384 | return; | ||
| 385 | default: | ||
| 386 | break; | ||
| 387 | } | ||
| 388 | |||
| 389 | fallback: | ||
| 390 | sb_add(m, "MAC="); | ||
| 391 | if (dev->hard_header_len && | ||
| 392 | skb->mac_header != skb->network_header) { | ||
| 393 | const unsigned char *p = skb_mac_header(skb); | ||
| 394 | unsigned int i; | ||
| 395 | |||
| 396 | sb_add(m, "%02x", *p++); | ||
| 397 | for (i = 1; i < dev->hard_header_len; i++, p++) | ||
| 398 | sb_add(m, ":%02x", *p); | ||
| 399 | } | ||
| 400 | sb_add(m, " "); | ||
| 401 | } | ||
| 402 | |||
| 403 | static struct nf_loginfo default_loginfo = { | ||
| 404 | .type = NF_LOG_TYPE_LOG, | ||
| 405 | .u = { | ||
| 406 | .log = { | ||
| 407 | .level = 5, | ||
| 408 | .logflags = NF_LOG_MASK, | ||
| 409 | }, | ||
| 410 | }, | ||
| 411 | }; | ||
| 412 | |||
| 413 | static void | ||
| 414 | ipt_log_packet(u_int8_t pf, | ||
| 415 | unsigned int hooknum, | ||
| 416 | const struct sk_buff *skb, | ||
| 417 | const struct net_device *in, | ||
| 418 | const struct net_device *out, | ||
| 419 | const struct nf_loginfo *loginfo, | ||
| 420 | const char *prefix) | ||
| 421 | { | ||
| 422 | struct sbuff *m = sb_open(); | ||
| 423 | |||
| 424 | if (!loginfo) | ||
| 425 | loginfo = &default_loginfo; | ||
| 426 | |||
| 427 | sb_add(m, "<%d>%sIN=%s OUT=%s ", loginfo->u.log.level, | ||
| 428 | prefix, | ||
| 429 | in ? in->name : "", | ||
| 430 | out ? out->name : ""); | ||
| 431 | #ifdef CONFIG_BRIDGE_NETFILTER | ||
| 432 | if (skb->nf_bridge) { | ||
| 433 | const struct net_device *physindev; | ||
| 434 | const struct net_device *physoutdev; | ||
| 435 | |||
| 436 | physindev = skb->nf_bridge->physindev; | ||
| 437 | if (physindev && in != physindev) | ||
| 438 | sb_add(m, "PHYSIN=%s ", physindev->name); | ||
| 439 | physoutdev = skb->nf_bridge->physoutdev; | ||
| 440 | if (physoutdev && out != physoutdev) | ||
| 441 | sb_add(m, "PHYSOUT=%s ", physoutdev->name); | ||
| 442 | } | ||
| 443 | #endif | ||
| 444 | |||
| 445 | if (in != NULL) | ||
| 446 | dump_mac_header(m, loginfo, skb); | ||
| 447 | |||
| 448 | dump_packet(m, loginfo, skb, 0); | ||
| 449 | |||
| 450 | sb_close(m); | ||
| 451 | } | ||
| 452 | |||
| 453 | static unsigned int | ||
| 454 | log_tg(struct sk_buff *skb, const struct xt_action_param *par) | ||
| 455 | { | ||
| 456 | const struct ipt_log_info *loginfo = par->targinfo; | ||
| 457 | struct nf_loginfo li; | ||
| 458 | |||
| 459 | li.type = NF_LOG_TYPE_LOG; | ||
| 460 | li.u.log.level = loginfo->level; | ||
| 461 | li.u.log.logflags = loginfo->logflags; | ||
| 462 | |||
| 463 | ipt_log_packet(NFPROTO_IPV4, par->hooknum, skb, par->in, par->out, &li, | ||
| 464 | loginfo->prefix); | ||
| 465 | return XT_CONTINUE; | ||
| 466 | } | ||
| 467 | |||
| 468 | static int log_tg_check(const struct xt_tgchk_param *par) | ||
| 469 | { | ||
| 470 | const struct ipt_log_info *loginfo = par->targinfo; | ||
| 471 | |||
| 472 | if (loginfo->level >= 8) { | ||
| 473 | pr_debug("level %u >= 8\n", loginfo->level); | ||
| 474 | return -EINVAL; | ||
| 475 | } | ||
| 476 | if (loginfo->prefix[sizeof(loginfo->prefix)-1] != '\0') { | ||
| 477 | pr_debug("prefix is not null-terminated\n"); | ||
| 478 | return -EINVAL; | ||
| 479 | } | ||
| 480 | return 0; | ||
| 481 | } | ||
| 482 | |||
| 483 | static struct xt_target log_tg_reg __read_mostly = { | ||
| 484 | .name = "LOG", | ||
| 485 | .family = NFPROTO_IPV4, | ||
| 486 | .target = log_tg, | ||
| 487 | .targetsize = sizeof(struct ipt_log_info), | ||
| 488 | .checkentry = log_tg_check, | ||
| 489 | .me = THIS_MODULE, | ||
| 490 | }; | ||
| 491 | |||
| 492 | static struct nf_logger ipt_log_logger __read_mostly = { | ||
| 493 | .name = "ipt_LOG", | ||
| 494 | .logfn = &ipt_log_packet, | ||
| 495 | .me = THIS_MODULE, | ||
| 496 | }; | ||
| 497 | |||
| 498 | static int __init log_tg_init(void) | ||
| 499 | { | ||
| 500 | int ret; | ||
| 501 | |||
| 502 | ret = xt_register_target(&log_tg_reg); | ||
| 503 | if (ret < 0) | ||
| 504 | return ret; | ||
| 505 | nf_log_register(NFPROTO_IPV4, &ipt_log_logger); | ||
| 506 | return 0; | ||
| 507 | } | ||
| 508 | |||
| 509 | static void __exit log_tg_exit(void) | ||
| 510 | { | ||
| 511 | nf_log_unregister(&ipt_log_logger); | ||
| 512 | xt_unregister_target(&log_tg_reg); | ||
| 513 | } | ||
| 514 | |||
| 515 | module_init(log_tg_init); | ||
| 516 | module_exit(log_tg_exit); | ||
diff --git a/net/ipv4/netfilter/ipt_NETMAP.c b/net/ipv4/netfilter/ipt_NETMAP.c new file mode 100644 index 00000000000..6cdb298f103 --- /dev/null +++ b/net/ipv4/netfilter/ipt_NETMAP.c | |||
| @@ -0,0 +1,98 @@ | |||
| 1 | /* NETMAP - static NAT mapping of IP network addresses (1:1). | ||
| 2 | * The mapping can be applied to source (POSTROUTING), | ||
| 3 | * destination (PREROUTING), or both (with separate rules). | ||
| 4 | */ | ||
| 5 | |||
| 6 | /* (C) 2000-2001 Svenning Soerensen <svenning@post5.tele.dk> | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or modify | ||
| 9 | * it under the terms of the GNU General Public License version 2 as | ||
| 10 | * published by the Free Software Foundation. | ||
| 11 | */ | ||
| 12 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
| 13 | #include <linux/ip.h> | ||
| 14 | #include <linux/module.h> | ||
| 15 | #include <linux/netdevice.h> | ||
| 16 | #include <linux/netfilter.h> | ||
| 17 | #include <linux/netfilter_ipv4.h> | ||
| 18 | #include <linux/netfilter/x_tables.h> | ||
| 19 | #include <net/netfilter/nf_nat_rule.h> | ||
| 20 | |||
| 21 | MODULE_LICENSE("GPL"); | ||
| 22 | MODULE_AUTHOR("Svenning Soerensen <svenning@post5.tele.dk>"); | ||
| 23 | MODULE_DESCRIPTION("Xtables: 1:1 NAT mapping of IPv4 subnets"); | ||
| 24 | |||
| 25 | static int netmap_tg_check(const struct xt_tgchk_param *par) | ||
| 26 | { | ||
| 27 | const struct nf_nat_multi_range_compat *mr = par->targinfo; | ||
| 28 | |||
| 29 | if (!(mr->range[0].flags & IP_NAT_RANGE_MAP_IPS)) { | ||
| 30 | pr_debug("bad MAP_IPS.\n"); | ||
| 31 | return -EINVAL; | ||
| 32 | } | ||
| 33 | if (mr->rangesize != 1) { | ||
| 34 | pr_debug("bad rangesize %u.\n", mr->rangesize); | ||
| 35 | return -EINVAL; | ||
| 36 | } | ||
| 37 | return 0; | ||
| 38 | } | ||
| 39 | |||
| 40 | static unsigned int | ||
| 41 | netmap_tg(struct sk_buff *skb, const struct xt_action_param *par) | ||
| 42 | { | ||
| 43 | struct nf_conn *ct; | ||
| 44 | enum ip_conntrack_info ctinfo; | ||
| 45 | __be32 new_ip, netmask; | ||
| 46 | const struct nf_nat_multi_range_compat *mr = par->targinfo; | ||
| 47 | struct nf_nat_range newrange; | ||
| 48 | |||
| 49 | NF_CT_ASSERT(par->hooknum == NF_INET_PRE_ROUTING || | ||
| 50 | par->hooknum == NF_INET_POST_ROUTING || | ||
| 51 | par->hooknum == NF_INET_LOCAL_OUT || | ||
| 52 | par->hooknum == NF_INET_LOCAL_IN); | ||
| 53 | ct = nf_ct_get(skb, &ctinfo); | ||
| 54 | |||
| 55 | netmask = ~(mr->range[0].min_ip ^ mr->range[0].max_ip); | ||
| 56 | |||
| 57 | if (par->hooknum == NF_INET_PRE_ROUTING || | ||
| 58 | par->hooknum == NF_INET_LOCAL_OUT) | ||
| 59 | new_ip = ip_hdr(skb)->daddr & ~netmask; | ||
| 60 | else | ||
| 61 | new_ip = ip_hdr(skb)->saddr & ~netmask; | ||
| 62 | new_ip |= mr->range[0].min_ip & netmask; | ||
| 63 | |||
| 64 | newrange = ((struct nf_nat_range) | ||
| 65 | { mr->range[0].flags | IP_NAT_RANGE_MAP_IPS, | ||
| 66 | new_ip, new_ip, | ||
| 67 | mr->range[0].min, mr->range[0].max }); | ||
| 68 | |||
| 69 | /* Hand modified range to generic setup. */ | ||
| 70 | return nf_nat_setup_info(ct, &newrange, HOOK2MANIP(par->hooknum)); | ||
| 71 | } | ||
| 72 | |||
| 73 | static struct xt_target netmap_tg_reg __read_mostly = { | ||
| 74 | .name = "NETMAP", | ||
| 75 | .family = NFPROTO_IPV4, | ||
| 76 | .target = netmap_tg, | ||
| 77 | .targetsize = sizeof(struct nf_nat_multi_range_compat), | ||
| 78 | .table = "nat", | ||
| 79 | .hooks = (1 << NF_INET_PRE_ROUTING) | | ||
| 80 | (1 << NF_INET_POST_ROUTING) | | ||
| 81 | (1 << NF_INET_LOCAL_OUT) | | ||
| 82 | (1 << NF_INET_LOCAL_IN), | ||
| 83 | .checkentry = netmap_tg_check, | ||
| 84 | .me = THIS_MODULE | ||
| 85 | }; | ||
| 86 | |||
| 87 | static int __init netmap_tg_init(void) | ||
| 88 | { | ||
| 89 | return xt_register_target(&netmap_tg_reg); | ||
| 90 | } | ||
| 91 | |||
| 92 | static void __exit netmap_tg_exit(void) | ||
| 93 | { | ||
| 94 | xt_unregister_target(&netmap_tg_reg); | ||
| 95 | } | ||
| 96 | |||
| 97 | module_init(netmap_tg_init); | ||
| 98 | module_exit(netmap_tg_exit); | ||
diff --git a/net/ipv4/netfilter/ipt_REDIRECT.c b/net/ipv4/netfilter/ipt_REDIRECT.c new file mode 100644 index 00000000000..18a0656505a --- /dev/null +++ b/net/ipv4/netfilter/ipt_REDIRECT.c | |||
| @@ -0,0 +1,110 @@ | |||
| 1 | /* Redirect. Simple mapping which alters dst to a local IP address. */ | ||
| 2 | /* (C) 1999-2001 Paul `Rusty' Russell | ||
| 3 | * (C) 2002-2006 Netfilter Core Team <coreteam@netfilter.org> | ||
| 4 | * | ||
| 5 | * This program is free software; you can redistribute it and/or modify | ||
| 6 | * it under the terms of the GNU General Public License version 2 as | ||
| 7 | * published by the Free Software Foundation. | ||
| 8 | */ | ||
| 9 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
| 10 | #include <linux/types.h> | ||
| 11 | #include <linux/ip.h> | ||
| 12 | #include <linux/timer.h> | ||
| 13 | #include <linux/module.h> | ||
| 14 | #include <linux/netfilter.h> | ||
| 15 | #include <linux/netdevice.h> | ||
| 16 | #include <linux/if.h> | ||
| 17 | #include <linux/inetdevice.h> | ||
| 18 | #include <net/protocol.h> | ||
| 19 | #include <net/checksum.h> | ||
| 20 | #include <linux/netfilter_ipv4.h> | ||
| 21 | #include <linux/netfilter/x_tables.h> | ||
| 22 | #include <net/netfilter/nf_nat_rule.h> | ||
| 23 | |||
| 24 | MODULE_LICENSE("GPL"); | ||
| 25 | MODULE_AUTHOR("Netfilter Core Team <coreteam@netfilter.org>"); | ||
| 26 | MODULE_DESCRIPTION("Xtables: Connection redirection to localhost"); | ||
| 27 | |||
| 28 | /* FIXME: Take multiple ranges --RR */ | ||
| 29 | static int redirect_tg_check(const struct xt_tgchk_param *par) | ||
| 30 | { | ||
| 31 | const struct nf_nat_multi_range_compat *mr = par->targinfo; | ||
| 32 | |||
| 33 | if (mr->range[0].flags & IP_NAT_RANGE_MAP_IPS) { | ||
| 34 | pr_debug("bad MAP_IPS.\n"); | ||
| 35 | return -EINVAL; | ||
| 36 | } | ||
| 37 | if (mr->rangesize != 1) { | ||
| 38 | pr_debug("bad rangesize %u.\n", mr->rangesize); | ||
| 39 | return -EINVAL; | ||
| 40 | } | ||
| 41 | return 0; | ||
| 42 | } | ||
| 43 | |||
| 44 | static unsigned int | ||
| 45 | redirect_tg(struct sk_buff *skb, const struct xt_action_param *par) | ||
| 46 | { | ||
| 47 | struct nf_conn *ct; | ||
| 48 | enum ip_conntrack_info ctinfo; | ||
| 49 | __be32 newdst; | ||
| 50 | const struct nf_nat_multi_range_compat *mr = par->targinfo; | ||
| 51 | struct nf_nat_range newrange; | ||
| 52 | |||
| 53 | NF_CT_ASSERT(par->hooknum == NF_INET_PRE_ROUTING || | ||
| 54 | par->hooknum == NF_INET_LOCAL_OUT); | ||
| 55 | |||
| 56 | ct = nf_ct_get(skb, &ctinfo); | ||
| 57 | NF_CT_ASSERT(ct && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED)); | ||
| 58 | |||
| 59 | /* Local packets: make them go to loopback */ | ||
| 60 | if (par->hooknum == NF_INET_LOCAL_OUT) | ||
| 61 | newdst = htonl(0x7F000001); | ||
| 62 | else { | ||
| 63 | struct in_device *indev; | ||
| 64 | struct in_ifaddr *ifa; | ||
| 65 | |||
| 66 | newdst = 0; | ||
| 67 | |||
| 68 | rcu_read_lock(); | ||
| 69 | indev = __in_dev_get_rcu(skb->dev); | ||
| 70 | if (indev && (ifa = indev->ifa_list)) | ||
| 71 | newdst = ifa->ifa_local; | ||
| 72 | rcu_read_unlock(); | ||
| 73 | |||
| 74 | if (!newdst) | ||
| 75 | return NF_DROP; | ||
| 76 | } | ||
| 77 | |||
| 78 | /* Transfer from original range. */ | ||
| 79 | newrange = ((struct nf_nat_range) | ||
| 80 | { mr->range[0].flags | IP_NAT_RANGE_MAP_IPS, | ||
| 81 | newdst, newdst, | ||
| 82 | mr->range[0].min, mr->range[0].max }); | ||
| 83 | |||
| 84 | /* Hand modified range to generic setup. */ | ||
| 85 | return nf_nat_setup_info(ct, &newrange, IP_NAT_MANIP_DST); | ||
| 86 | } | ||
| 87 | |||
| 88 | static struct xt_target redirect_tg_reg __read_mostly = { | ||
| 89 | .name = "REDIRECT", | ||
| 90 | .family = NFPROTO_IPV4, | ||
| 91 | .target = redirect_tg, | ||
| 92 | .targetsize = sizeof(struct nf_nat_multi_range_compat), | ||
| 93 | .table = "nat", | ||
| 94 | .hooks = (1 << NF_INET_PRE_ROUTING) | (1 << NF_INET_LOCAL_OUT), | ||
| 95 | .checkentry = redirect_tg_check, | ||
| 96 | .me = THIS_MODULE, | ||
| 97 | }; | ||
| 98 | |||
| 99 | static int __init redirect_tg_init(void) | ||
| 100 | { | ||
| 101 | return xt_register_target(&redirect_tg_reg); | ||
| 102 | } | ||
| 103 | |||
| 104 | static void __exit redirect_tg_exit(void) | ||
| 105 | { | ||
| 106 | xt_unregister_target(&redirect_tg_reg); | ||
| 107 | } | ||
| 108 | |||
| 109 | module_init(redirect_tg_init); | ||
| 110 | module_exit(redirect_tg_exit); | ||
diff --git a/net/ipv4/netfilter/ipt_ecn.c b/net/ipv4/netfilter/ipt_ecn.c new file mode 100644 index 00000000000..2b57e52c746 --- /dev/null +++ b/net/ipv4/netfilter/ipt_ecn.c | |||
| @@ -0,0 +1,127 @@ | |||
| 1 | /* IP tables module for matching the value of the IPv4 and TCP ECN bits | ||
| 2 | * | ||
| 3 | * (C) 2002 by Harald Welte <laforge@gnumonks.org> | ||
| 4 | * | ||
| 5 | * This program is free software; you can redistribute it and/or modify | ||
| 6 | * it under the terms of the GNU General Public License version 2 as | ||
| 7 | * published by the Free Software Foundation. | ||
| 8 | */ | ||
| 9 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
| 10 | #include <linux/in.h> | ||
| 11 | #include <linux/ip.h> | ||
| 12 | #include <net/ip.h> | ||
| 13 | #include <linux/module.h> | ||
| 14 | #include <linux/skbuff.h> | ||
| 15 | #include <linux/tcp.h> | ||
| 16 | |||
| 17 | #include <linux/netfilter/x_tables.h> | ||
| 18 | #include <linux/netfilter_ipv4/ip_tables.h> | ||
| 19 | #include <linux/netfilter_ipv4/ipt_ecn.h> | ||
| 20 | |||
| 21 | MODULE_AUTHOR("Harald Welte <laforge@netfilter.org>"); | ||
| 22 | MODULE_DESCRIPTION("Xtables: Explicit Congestion Notification (ECN) flag match for IPv4"); | ||
| 23 | MODULE_LICENSE("GPL"); | ||
| 24 | |||
| 25 | static inline bool match_ip(const struct sk_buff *skb, | ||
| 26 | const struct ipt_ecn_info *einfo) | ||
| 27 | { | ||
| 28 | return ((ip_hdr(skb)->tos & IPT_ECN_IP_MASK) == einfo->ip_ect) ^ | ||
| 29 | !!(einfo->invert & IPT_ECN_OP_MATCH_IP); | ||
| 30 | } | ||
| 31 | |||
| 32 | static inline bool match_tcp(const struct sk_buff *skb, | ||
| 33 | const struct ipt_ecn_info *einfo, | ||
| 34 | bool *hotdrop) | ||
| 35 | { | ||
| 36 | struct tcphdr _tcph; | ||
| 37 | const struct tcphdr *th; | ||
| 38 | |||
| 39 | /* In practice, TCP match does this, so can't fail. But let's | ||
| 40 | * be good citizens. | ||
| 41 | */ | ||
| 42 | th = skb_header_pointer(skb, ip_hdrlen(skb), sizeof(_tcph), &_tcph); | ||
| 43 | if (th == NULL) { | ||
| 44 | *hotdrop = false; | ||
| 45 | return false; | ||
| 46 | } | ||
| 47 | |||
| 48 | if (einfo->operation & IPT_ECN_OP_MATCH_ECE) { | ||
| 49 | if (einfo->invert & IPT_ECN_OP_MATCH_ECE) { | ||
| 50 | if (th->ece == 1) | ||
| 51 | return false; | ||
| 52 | } else { | ||
| 53 | if (th->ece == 0) | ||
| 54 | return false; | ||
| 55 | } | ||
| 56 | } | ||
| 57 | |||
| 58 | if (einfo->operation & IPT_ECN_OP_MATCH_CWR) { | ||
| 59 | if (einfo->invert & IPT_ECN_OP_MATCH_CWR) { | ||
| 60 | if (th->cwr == 1) | ||
| 61 | return false; | ||
| 62 | } else { | ||
| 63 | if (th->cwr == 0) | ||
| 64 | return false; | ||
| 65 | } | ||
| 66 | } | ||
| 67 | |||
| 68 | return true; | ||
| 69 | } | ||
| 70 | |||
| 71 | static bool ecn_mt(const struct sk_buff *skb, struct xt_action_param *par) | ||
| 72 | { | ||
| 73 | const struct ipt_ecn_info *info = par->matchinfo; | ||
| 74 | |||
| 75 | if (info->operation & IPT_ECN_OP_MATCH_IP) | ||
| 76 | if (!match_ip(skb, info)) | ||
| 77 | return false; | ||
| 78 | |||
| 79 | if (info->operation & (IPT_ECN_OP_MATCH_ECE|IPT_ECN_OP_MATCH_CWR)) { | ||
| 80 | if (!match_tcp(skb, info, &par->hotdrop)) | ||
| 81 | return false; | ||
| 82 | } | ||
| 83 | |||
| 84 | return true; | ||
| 85 | } | ||
| 86 | |||
| 87 | static int ecn_mt_check(const struct xt_mtchk_param *par) | ||
| 88 | { | ||
| 89 | const struct ipt_ecn_info *info = par->matchinfo; | ||
| 90 | const struct ipt_ip *ip = par->entryinfo; | ||
| 91 | |||
| 92 | if (info->operation & IPT_ECN_OP_MATCH_MASK) | ||
| 93 | return -EINVAL; | ||
| 94 | |||
| 95 | if (info->invert & IPT_ECN_OP_MATCH_MASK) | ||
| 96 | return -EINVAL; | ||
| 97 | |||
| 98 | if (info->operation & (IPT_ECN_OP_MATCH_ECE|IPT_ECN_OP_MATCH_CWR) && | ||
| 99 | (ip->proto != IPPROTO_TCP || ip->invflags & IPT_INV_PROTO)) { | ||
| 100 | pr_info("cannot match TCP bits in rule for non-tcp packets\n"); | ||
| 101 | return -EINVAL; | ||
| 102 | } | ||
| 103 | |||
| 104 | return 0; | ||
| 105 | } | ||
| 106 | |||
| 107 | static struct xt_match ecn_mt_reg __read_mostly = { | ||
| 108 | .name = "ecn", | ||
| 109 | .family = NFPROTO_IPV4, | ||
| 110 | .match = ecn_mt, | ||
| 111 | .matchsize = sizeof(struct ipt_ecn_info), | ||
| 112 | .checkentry = ecn_mt_check, | ||
| 113 | .me = THIS_MODULE, | ||
| 114 | }; | ||
| 115 | |||
| 116 | static int __init ecn_mt_init(void) | ||
| 117 | { | ||
| 118 | return xt_register_match(&ecn_mt_reg); | ||
| 119 | } | ||
| 120 | |||
| 121 | static void __exit ecn_mt_exit(void) | ||
| 122 | { | ||
| 123 | xt_unregister_match(&ecn_mt_reg); | ||
| 124 | } | ||
| 125 | |||
| 126 | module_init(ecn_mt_init); | ||
| 127 | module_exit(ecn_mt_exit); | ||
diff --git a/net/ipv4/netfilter/nf_nat_amanda.c b/net/ipv4/netfilter/nf_nat_amanda.c new file mode 100644 index 00000000000..703f366fd23 --- /dev/null +++ b/net/ipv4/netfilter/nf_nat_amanda.c | |||
| @@ -0,0 +1,85 @@ | |||
| 1 | /* Amanda extension for TCP NAT alteration. | ||
| 2 | * (C) 2002 by Brian J. Murrell <netfilter@interlinx.bc.ca> | ||
| 3 | * based on a copy of HW's ip_nat_irc.c as well as other modules | ||
| 4 | * | ||
| 5 | * This program is free software; you can redistribute it and/or | ||
| 6 | * modify it under the terms of the GNU General Public License | ||
| 7 | * as published by the Free Software Foundation; either version | ||
| 8 | * 2 of the License, or (at your option) any later version. | ||
| 9 | */ | ||
| 10 | |||
| 11 | #include <linux/kernel.h> | ||
| 12 | #include <linux/module.h> | ||
| 13 | #include <linux/skbuff.h> | ||
| 14 | #include <linux/udp.h> | ||
| 15 | |||
| 16 | #include <net/netfilter/nf_nat_helper.h> | ||
| 17 | #include <net/netfilter/nf_nat_rule.h> | ||
| 18 | #include <net/netfilter/nf_conntrack_helper.h> | ||
| 19 | #include <net/netfilter/nf_conntrack_expect.h> | ||
| 20 | #include <linux/netfilter/nf_conntrack_amanda.h> | ||
| 21 | |||
| 22 | MODULE_AUTHOR("Brian J. Murrell <netfilter@interlinx.bc.ca>"); | ||
| 23 | MODULE_DESCRIPTION("Amanda NAT helper"); | ||
| 24 | MODULE_LICENSE("GPL"); | ||
| 25 | MODULE_ALIAS("ip_nat_amanda"); | ||
| 26 | |||
| 27 | static unsigned int help(struct sk_buff *skb, | ||
| 28 | enum ip_conntrack_info ctinfo, | ||
| 29 | unsigned int matchoff, | ||
| 30 | unsigned int matchlen, | ||
| 31 | struct nf_conntrack_expect *exp) | ||
| 32 | { | ||
| 33 | char buffer[sizeof("65535")]; | ||
| 34 | u_int16_t port; | ||
| 35 | unsigned int ret; | ||
| 36 | |||
| 37 | /* Connection comes from client. */ | ||
| 38 | exp->saved_proto.tcp.port = exp->tuple.dst.u.tcp.port; | ||
| 39 | exp->dir = IP_CT_DIR_ORIGINAL; | ||
| 40 | |||
| 41 | /* When you see the packet, we need to NAT it the same as the | ||
| 42 | * this one (ie. same IP: it will be TCP and master is UDP). */ | ||
| 43 | exp->expectfn = nf_nat_follow_master; | ||
| 44 | |||
| 45 | /* Try to get same port: if not, try to change it. */ | ||
| 46 | for (port = ntohs(exp->saved_proto.tcp.port); port != 0; port++) { | ||
| 47 | int res; | ||
| 48 | |||
| 49 | exp->tuple.dst.u.tcp.port = htons(port); | ||
| 50 | res = nf_ct_expect_related(exp); | ||
| 51 | if (res == 0) | ||
| 52 | break; | ||
| 53 | else if (res != -EBUSY) { | ||
| 54 | port = 0; | ||
| 55 | break; | ||
| 56 | } | ||
| 57 | } | ||
| 58 | |||
| 59 | if (port == 0) | ||
| 60 | return NF_DROP; | ||
| 61 | |||
| 62 | sprintf(buffer, "%u", port); | ||
| 63 | ret = nf_nat_mangle_udp_packet(skb, exp->master, ctinfo, | ||
| 64 | matchoff, matchlen, | ||
| 65 | buffer, strlen(buffer)); | ||
| 66 | if (ret != NF_ACCEPT) | ||
| 67 | nf_ct_unexpect_related(exp); | ||
| 68 | return ret; | ||
| 69 | } | ||
| 70 | |||
| 71 | static void __exit nf_nat_amanda_fini(void) | ||
| 72 | { | ||
| 73 | rcu_assign_pointer(nf_nat_amanda_hook, NULL); | ||
| 74 | synchronize_rcu(); | ||
| 75 | } | ||
| 76 | |||
| 77 | static int __init nf_nat_amanda_init(void) | ||
| 78 | { | ||
| 79 | BUG_ON(nf_nat_amanda_hook != NULL); | ||
| 80 | rcu_assign_pointer(nf_nat_amanda_hook, help); | ||
| 81 | return 0; | ||
| 82 | } | ||
| 83 | |||
| 84 | module_init(nf_nat_amanda_init); | ||
| 85 | module_exit(nf_nat_amanda_fini); | ||
diff --git a/net/ipv4/netfilter/nf_nat_core.c b/net/ipv4/netfilter/nf_nat_core.c new file mode 100644 index 00000000000..3346de5d94d --- /dev/null +++ b/net/ipv4/netfilter/nf_nat_core.c | |||
| @@ -0,0 +1,779 @@ | |||
| 1 | /* NAT for netfilter; shared with compatibility layer. */ | ||
| 2 | |||
| 3 | /* (C) 1999-2001 Paul `Rusty' Russell | ||
| 4 | * (C) 2002-2006 Netfilter Core Team <coreteam@netfilter.org> | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU General Public License version 2 as | ||
| 8 | * published by the Free Software Foundation. | ||
| 9 | */ | ||
| 10 | |||
| 11 | #include <linux/module.h> | ||
| 12 | #include <linux/types.h> | ||
| 13 | #include <linux/timer.h> | ||
| 14 | #include <linux/skbuff.h> | ||
| 15 | #include <linux/gfp.h> | ||
| 16 | #include <net/checksum.h> | ||
| 17 | #include <net/icmp.h> | ||
| 18 | #include <net/ip.h> | ||
| 19 | #include <net/tcp.h> /* For tcp_prot in getorigdst */ | ||
| 20 | #include <linux/icmp.h> | ||
| 21 | #include <linux/udp.h> | ||
| 22 | #include <linux/jhash.h> | ||
| 23 | |||
| 24 | #include <linux/netfilter_ipv4.h> | ||
| 25 | #include <net/netfilter/nf_conntrack.h> | ||
| 26 | #include <net/netfilter/nf_conntrack_core.h> | ||
| 27 | #include <net/netfilter/nf_nat.h> | ||
| 28 | #include <net/netfilter/nf_nat_protocol.h> | ||
| 29 | #include <net/netfilter/nf_nat_core.h> | ||
| 30 | #include <net/netfilter/nf_nat_helper.h> | ||
| 31 | #include <net/netfilter/nf_conntrack_helper.h> | ||
| 32 | #include <net/netfilter/nf_conntrack_l3proto.h> | ||
| 33 | #include <net/netfilter/nf_conntrack_l4proto.h> | ||
| 34 | #include <net/netfilter/nf_conntrack_zones.h> | ||
| 35 | |||
| 36 | static DEFINE_SPINLOCK(nf_nat_lock); | ||
| 37 | |||
| 38 | static struct nf_conntrack_l3proto *l3proto __read_mostly; | ||
| 39 | |||
| 40 | #define MAX_IP_NAT_PROTO 256 | ||
| 41 | static const struct nf_nat_protocol __rcu *nf_nat_protos[MAX_IP_NAT_PROTO] | ||
| 42 | __read_mostly; | ||
| 43 | |||
| 44 | static inline const struct nf_nat_protocol * | ||
| 45 | __nf_nat_proto_find(u_int8_t protonum) | ||
| 46 | { | ||
| 47 | return rcu_dereference(nf_nat_protos[protonum]); | ||
| 48 | } | ||
| 49 | |||
| 50 | /* We keep an extra hash for each conntrack, for fast searching. */ | ||
| 51 | static inline unsigned int | ||
| 52 | hash_by_src(const struct net *net, u16 zone, | ||
| 53 | const struct nf_conntrack_tuple *tuple) | ||
| 54 | { | ||
| 55 | unsigned int hash; | ||
| 56 | |||
| 57 | /* Original src, to ensure we map it consistently if poss. */ | ||
| 58 | hash = jhash_3words((__force u32)tuple->src.u3.ip, | ||
| 59 | (__force u32)tuple->src.u.all ^ zone, | ||
| 60 | tuple->dst.protonum, 0); | ||
| 61 | return ((u64)hash * net->ipv4.nat_htable_size) >> 32; | ||
| 62 | } | ||
| 63 | |||
| 64 | /* Is this tuple already taken? (not by us) */ | ||
| 65 | int | ||
| 66 | nf_nat_used_tuple(const struct nf_conntrack_tuple *tuple, | ||
| 67 | const struct nf_conn *ignored_conntrack) | ||
| 68 | { | ||
| 69 | /* Conntrack tracking doesn't keep track of outgoing tuples; only | ||
| 70 | incoming ones. NAT means they don't have a fixed mapping, | ||
| 71 | so we invert the tuple and look for the incoming reply. | ||
| 72 | |||
| 73 | We could keep a separate hash if this proves too slow. */ | ||
| 74 | struct nf_conntrack_tuple reply; | ||
| 75 | |||
| 76 | nf_ct_invert_tuplepr(&reply, tuple); | ||
| 77 | return nf_conntrack_tuple_taken(&reply, ignored_conntrack); | ||
| 78 | } | ||
| 79 | EXPORT_SYMBOL(nf_nat_used_tuple); | ||
| 80 | |||
| 81 | /* If we source map this tuple so reply looks like reply_tuple, will | ||
| 82 | * that meet the constraints of range. */ | ||
| 83 | static int | ||
| 84 | in_range(const struct nf_conntrack_tuple *tuple, | ||
| 85 | const struct nf_nat_range *range) | ||
| 86 | { | ||
| 87 | const struct nf_nat_protocol *proto; | ||
| 88 | int ret = 0; | ||
| 89 | |||
| 90 | /* If we are supposed to map IPs, then we must be in the | ||
| 91 | range specified, otherwise let this drag us onto a new src IP. */ | ||
| 92 | if (range->flags & IP_NAT_RANGE_MAP_IPS) { | ||
| 93 | if (ntohl(tuple->src.u3.ip) < ntohl(range->min_ip) || | ||
| 94 | ntohl(tuple->src.u3.ip) > ntohl(range->max_ip)) | ||
| 95 | return 0; | ||
| 96 | } | ||
| 97 | |||
| 98 | rcu_read_lock(); | ||
| 99 | proto = __nf_nat_proto_find(tuple->dst.protonum); | ||
| 100 | if (!(range->flags & IP_NAT_RANGE_PROTO_SPECIFIED) || | ||
| 101 | proto->in_range(tuple, IP_NAT_MANIP_SRC, | ||
| 102 | &range->min, &range->max)) | ||
| 103 | ret = 1; | ||
| 104 | rcu_read_unlock(); | ||
| 105 | |||
| 106 | return ret; | ||
| 107 | } | ||
| 108 | |||
| 109 | static inline int | ||
| 110 | same_src(const struct nf_conn *ct, | ||
| 111 | const struct nf_conntrack_tuple *tuple) | ||
| 112 | { | ||
| 113 | const struct nf_conntrack_tuple *t; | ||
| 114 | |||
| 115 | t = &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple; | ||
| 116 | return (t->dst.protonum == tuple->dst.protonum && | ||
| 117 | t->src.u3.ip == tuple->src.u3.ip && | ||
| 118 | t->src.u.all == tuple->src.u.all); | ||
| 119 | } | ||
| 120 | |||
| 121 | /* Only called for SRC manip */ | ||
| 122 | static int | ||
| 123 | find_appropriate_src(struct net *net, u16 zone, | ||
| 124 | const struct nf_conntrack_tuple *tuple, | ||
| 125 | struct nf_conntrack_tuple *result, | ||
| 126 | const struct nf_nat_range *range) | ||
| 127 | { | ||
| 128 | unsigned int h = hash_by_src(net, zone, tuple); | ||
| 129 | const struct nf_conn_nat *nat; | ||
| 130 | const struct nf_conn *ct; | ||
| 131 | const struct hlist_node *n; | ||
| 132 | |||
| 133 | rcu_read_lock(); | ||
| 134 | hlist_for_each_entry_rcu(nat, n, &net->ipv4.nat_bysource[h], bysource) { | ||
| 135 | ct = nat->ct; | ||
| 136 | if (same_src(ct, tuple) && nf_ct_zone(ct) == zone) { | ||
| 137 | /* Copy source part from reply tuple. */ | ||
| 138 | nf_ct_invert_tuplepr(result, | ||
| 139 | &ct->tuplehash[IP_CT_DIR_REPLY].tuple); | ||
| 140 | result->dst = tuple->dst; | ||
| 141 | |||
| 142 | if (in_range(result, range)) { | ||
| 143 | rcu_read_unlock(); | ||
| 144 | return 1; | ||
| 145 | } | ||
| 146 | } | ||
| 147 | } | ||
| 148 | rcu_read_unlock(); | ||
| 149 | return 0; | ||
| 150 | } | ||
| 151 | |||
| 152 | /* For [FUTURE] fragmentation handling, we want the least-used | ||
| 153 | src-ip/dst-ip/proto triple. Fairness doesn't come into it. Thus | ||
| 154 | if the range specifies 1.2.3.4 ports 10000-10005 and 1.2.3.5 ports | ||
| 155 | 1-65535, we don't do pro-rata allocation based on ports; we choose | ||
| 156 | the ip with the lowest src-ip/dst-ip/proto usage. | ||
| 157 | */ | ||
| 158 | static void | ||
| 159 | find_best_ips_proto(u16 zone, struct nf_conntrack_tuple *tuple, | ||
| 160 | const struct nf_nat_range *range, | ||
| 161 | const struct nf_conn *ct, | ||
| 162 | enum nf_nat_manip_type maniptype) | ||
| 163 | { | ||
| 164 | __be32 *var_ipp; | ||
| 165 | /* Host order */ | ||
| 166 | u_int32_t minip, maxip, j; | ||
| 167 | |||
| 168 | /* No IP mapping? Do nothing. */ | ||
| 169 | if (!(range->flags & IP_NAT_RANGE_MAP_IPS)) | ||
| 170 | return; | ||
| 171 | |||
| 172 | if (maniptype == IP_NAT_MANIP_SRC) | ||
| 173 | var_ipp = &tuple->src.u3.ip; | ||
| 174 | else | ||
| 175 | var_ipp = &tuple->dst.u3.ip; | ||
| 176 | |||
| 177 | /* Fast path: only one choice. */ | ||
| 178 | if (range->min_ip == range->max_ip) { | ||
| 179 | *var_ipp = range->min_ip; | ||
| 180 | return; | ||
| 181 | } | ||
| 182 | |||
| 183 | /* Hashing source and destination IPs gives a fairly even | ||
| 184 | * spread in practice (if there are a small number of IPs | ||
| 185 | * involved, there usually aren't that many connections | ||
| 186 | * anyway). The consistency means that servers see the same | ||
| 187 | * client coming from the same IP (some Internet Banking sites | ||
| 188 | * like this), even across reboots. */ | ||
| 189 | minip = ntohl(range->min_ip); | ||
| 190 | maxip = ntohl(range->max_ip); | ||
| 191 | j = jhash_2words((__force u32)tuple->src.u3.ip, | ||
| 192 | range->flags & IP_NAT_RANGE_PERSISTENT ? | ||
| 193 | 0 : (__force u32)tuple->dst.u3.ip ^ zone, 0); | ||
| 194 | j = ((u64)j * (maxip - minip + 1)) >> 32; | ||
| 195 | *var_ipp = htonl(minip + j); | ||
| 196 | } | ||
| 197 | |||
| 198 | /* Manipulate the tuple into the range given. For NF_INET_POST_ROUTING, | ||
| 199 | * we change the source to map into the range. For NF_INET_PRE_ROUTING | ||
| 200 | * and NF_INET_LOCAL_OUT, we change the destination to map into the | ||
| 201 | * range. It might not be possible to get a unique tuple, but we try. | ||
| 202 | * At worst (or if we race), we will end up with a final duplicate in | ||
| 203 | * __ip_conntrack_confirm and drop the packet. */ | ||
| 204 | static void | ||
| 205 | get_unique_tuple(struct nf_conntrack_tuple *tuple, | ||
| 206 | const struct nf_conntrack_tuple *orig_tuple, | ||
| 207 | const struct nf_nat_range *range, | ||
| 208 | struct nf_conn *ct, | ||
| 209 | enum nf_nat_manip_type maniptype) | ||
| 210 | { | ||
| 211 | struct net *net = nf_ct_net(ct); | ||
| 212 | const struct nf_nat_protocol *proto; | ||
| 213 | u16 zone = nf_ct_zone(ct); | ||
| 214 | |||
| 215 | /* 1) If this srcip/proto/src-proto-part is currently mapped, | ||
| 216 | and that same mapping gives a unique tuple within the given | ||
| 217 | range, use that. | ||
| 218 | |||
| 219 | This is only required for source (ie. NAT/masq) mappings. | ||
| 220 | So far, we don't do local source mappings, so multiple | ||
| 221 | manips not an issue. */ | ||
| 222 | if (maniptype == IP_NAT_MANIP_SRC && | ||
| 223 | !(range->flags & IP_NAT_RANGE_PROTO_RANDOM)) { | ||
| 224 | /* try the original tuple first */ | ||
| 225 | if (in_range(orig_tuple, range)) { | ||
| 226 | if (!nf_nat_used_tuple(orig_tuple, ct)) { | ||
| 227 | *tuple = *orig_tuple; | ||
| 228 | return; | ||
| 229 | } | ||
| 230 | } else if (find_appropriate_src(net, zone, orig_tuple, tuple, | ||
| 231 | range)) { | ||
| 232 | pr_debug("get_unique_tuple: Found current src map\n"); | ||
| 233 | if (!nf_nat_used_tuple(tuple, ct)) | ||
| 234 | return; | ||
| 235 | } | ||
| 236 | } | ||
| 237 | |||
| 238 | /* 2) Select the least-used IP/proto combination in the given | ||
| 239 | range. */ | ||
| 240 | *tuple = *orig_tuple; | ||
| 241 | find_best_ips_proto(zone, tuple, range, ct, maniptype); | ||
| 242 | |||
| 243 | /* 3) The per-protocol part of the manip is made to map into | ||
| 244 | the range to make a unique tuple. */ | ||
| 245 | |||
| 246 | rcu_read_lock(); | ||
| 247 | proto = __nf_nat_proto_find(orig_tuple->dst.protonum); | ||
| 248 | |||
| 249 | /* Only bother mapping if it's not already in range and unique */ | ||
| 250 | if (!(range->flags & IP_NAT_RANGE_PROTO_RANDOM)) { | ||
| 251 | if (range->flags & IP_NAT_RANGE_PROTO_SPECIFIED) { | ||
| 252 | if (proto->in_range(tuple, maniptype, &range->min, | ||
| 253 | &range->max) && | ||
| 254 | (range->min.all == range->max.all || | ||
| 255 | !nf_nat_used_tuple(tuple, ct))) | ||
| 256 | goto out; | ||
| 257 | } else if (!nf_nat_used_tuple(tuple, ct)) { | ||
| 258 | goto out; | ||
| 259 | } | ||
| 260 | } | ||
| 261 | |||
| 262 | /* Last change: get protocol to try to obtain unique tuple. */ | ||
| 263 | proto->unique_tuple(tuple, range, maniptype, ct); | ||
| 264 | out: | ||
| 265 | rcu_read_unlock(); | ||
| 266 | } | ||
| 267 | |||
| 268 | unsigned int | ||
| 269 | nf_nat_setup_info(struct nf_conn *ct, | ||
| 270 | const struct nf_nat_range *range, | ||
| 271 | enum nf_nat_manip_type maniptype) | ||
| 272 | { | ||
| 273 | struct net *net = nf_ct_net(ct); | ||
| 274 | struct nf_conntrack_tuple curr_tuple, new_tuple; | ||
| 275 | struct nf_conn_nat *nat; | ||
| 276 | |||
| 277 | /* nat helper or nfctnetlink also setup binding */ | ||
| 278 | nat = nfct_nat(ct); | ||
| 279 | if (!nat) { | ||
| 280 | nat = nf_ct_ext_add(ct, NF_CT_EXT_NAT, GFP_ATOMIC); | ||
| 281 | if (nat == NULL) { | ||
| 282 | pr_debug("failed to add NAT extension\n"); | ||
| 283 | return NF_ACCEPT; | ||
| 284 | } | ||
| 285 | } | ||
| 286 | |||
| 287 | NF_CT_ASSERT(maniptype == IP_NAT_MANIP_SRC || | ||
| 288 | maniptype == IP_NAT_MANIP_DST); | ||
| 289 | BUG_ON(nf_nat_initialized(ct, maniptype)); | ||
| 290 | |||
| 291 | /* What we've got will look like inverse of reply. Normally | ||
| 292 | this is what is in the conntrack, except for prior | ||
| 293 | manipulations (future optimization: if num_manips == 0, | ||
| 294 | orig_tp = | ||
| 295 | conntrack->tuplehash[IP_CT_DIR_ORIGINAL].tuple) */ | ||
| 296 | nf_ct_invert_tuplepr(&curr_tuple, | ||
| 297 | &ct->tuplehash[IP_CT_DIR_REPLY].tuple); | ||
| 298 | |||
| 299 | get_unique_tuple(&new_tuple, &curr_tuple, range, ct, maniptype); | ||
| 300 | |||
| 301 | if (!nf_ct_tuple_equal(&new_tuple, &curr_tuple)) { | ||
| 302 | struct nf_conntrack_tuple reply; | ||
| 303 | |||
| 304 | /* Alter conntrack table so will recognize replies. */ | ||
| 305 | nf_ct_invert_tuplepr(&reply, &new_tuple); | ||
| 306 | nf_conntrack_alter_reply(ct, &reply); | ||
| 307 | |||
| 308 | /* Non-atomic: we own this at the moment. */ | ||
| 309 | if (maniptype == IP_NAT_MANIP_SRC) | ||
| 310 | ct->status |= IPS_SRC_NAT; | ||
| 311 | else | ||
| 312 | ct->status |= IPS_DST_NAT; | ||
| 313 | } | ||
| 314 | |||
| 315 | if (maniptype == IP_NAT_MANIP_SRC) { | ||
| 316 | unsigned int srchash; | ||
| 317 | |||
| 318 | srchash = hash_by_src(net, nf_ct_zone(ct), | ||
| 319 | &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple); | ||
| 320 | spin_lock_bh(&nf_nat_lock); | ||
| 321 | /* nf_conntrack_alter_reply might re-allocate exntension aera */ | ||
| 322 | nat = nfct_nat(ct); | ||
| 323 | nat->ct = ct; | ||
| 324 | hlist_add_head_rcu(&nat->bysource, | ||
| 325 | &net->ipv4.nat_bysource[srchash]); | ||
| 326 | spin_unlock_bh(&nf_nat_lock); | ||
| 327 | } | ||
| 328 | |||
| 329 | /* It's done. */ | ||
| 330 | if (maniptype == IP_NAT_MANIP_DST) | ||
| 331 | ct->status |= IPS_DST_NAT_DONE; | ||
| 332 | else | ||
| 333 | ct->status |= IPS_SRC_NAT_DONE; | ||
| 334 | |||
| 335 | return NF_ACCEPT; | ||
| 336 | } | ||
| 337 | EXPORT_SYMBOL(nf_nat_setup_info); | ||
| 338 | |||
| 339 | /* Returns true if succeeded. */ | ||
| 340 | static bool | ||
| 341 | manip_pkt(u_int16_t proto, | ||
| 342 | struct sk_buff *skb, | ||
| 343 | unsigned int iphdroff, | ||
| 344 | const struct nf_conntrack_tuple *target, | ||
| 345 | enum nf_nat_manip_type maniptype) | ||
| 346 | { | ||
| 347 | struct iphdr *iph; | ||
| 348 | const struct nf_nat_protocol *p; | ||
| 349 | |||
| 350 | if (!skb_make_writable(skb, iphdroff + sizeof(*iph))) | ||
| 351 | return false; | ||
| 352 | |||
| 353 | iph = (void *)skb->data + iphdroff; | ||
| 354 | |||
| 355 | /* Manipulate protcol part. */ | ||
| 356 | |||
| 357 | /* rcu_read_lock()ed by nf_hook_slow */ | ||
| 358 | p = __nf_nat_proto_find(proto); | ||
| 359 | if (!p->manip_pkt(skb, iphdroff, target, maniptype)) | ||
| 360 | return false; | ||
| 361 | |||
| 362 | iph = (void *)skb->data + iphdroff; | ||
| 363 | |||
| 364 | if (maniptype == IP_NAT_MANIP_SRC) { | ||
| 365 | csum_replace4(&iph->check, iph->saddr, target->src.u3.ip); | ||
| 366 | iph->saddr = target->src.u3.ip; | ||
| 367 | } else { | ||
| 368 | csum_replace4(&iph->check, iph->daddr, target->dst.u3.ip); | ||
| 369 | iph->daddr = target->dst.u3.ip; | ||
| 370 | } | ||
| 371 | return true; | ||
| 372 | } | ||
| 373 | |||
| 374 | /* Do packet manipulations according to nf_nat_setup_info. */ | ||
| 375 | unsigned int nf_nat_packet(struct nf_conn *ct, | ||
| 376 | enum ip_conntrack_info ctinfo, | ||
| 377 | unsigned int hooknum, | ||
| 378 | struct sk_buff *skb) | ||
| 379 | { | ||
| 380 | enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); | ||
| 381 | unsigned long statusbit; | ||
| 382 | enum nf_nat_manip_type mtype = HOOK2MANIP(hooknum); | ||
| 383 | |||
| 384 | if (mtype == IP_NAT_MANIP_SRC) | ||
| 385 | statusbit = IPS_SRC_NAT; | ||
| 386 | else | ||
| 387 | statusbit = IPS_DST_NAT; | ||
| 388 | |||
| 389 | /* Invert if this is reply dir. */ | ||
| 390 | if (dir == IP_CT_DIR_REPLY) | ||
| 391 | statusbit ^= IPS_NAT_MASK; | ||
| 392 | |||
| 393 | /* Non-atomic: these bits don't change. */ | ||
| 394 | if (ct->status & statusbit) { | ||
| 395 | struct nf_conntrack_tuple target; | ||
| 396 | |||
| 397 | /* We are aiming to look like inverse of other direction. */ | ||
| 398 | nf_ct_invert_tuplepr(&target, &ct->tuplehash[!dir].tuple); | ||
| 399 | |||
| 400 | if (!manip_pkt(target.dst.protonum, skb, 0, &target, mtype)) | ||
| 401 | return NF_DROP; | ||
| 402 | } | ||
| 403 | return NF_ACCEPT; | ||
| 404 | } | ||
| 405 | EXPORT_SYMBOL_GPL(nf_nat_packet); | ||
| 406 | |||
| 407 | /* Dir is direction ICMP is coming from (opposite to packet it contains) */ | ||
| 408 | int nf_nat_icmp_reply_translation(struct nf_conn *ct, | ||
| 409 | enum ip_conntrack_info ctinfo, | ||
| 410 | unsigned int hooknum, | ||
| 411 | struct sk_buff *skb) | ||
| 412 | { | ||
| 413 | struct { | ||
| 414 | struct icmphdr icmp; | ||
| 415 | struct iphdr ip; | ||
| 416 | } *inside; | ||
| 417 | const struct nf_conntrack_l4proto *l4proto; | ||
| 418 | struct nf_conntrack_tuple inner, target; | ||
| 419 | int hdrlen = ip_hdrlen(skb); | ||
| 420 | enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); | ||
| 421 | unsigned long statusbit; | ||
| 422 | enum nf_nat_manip_type manip = HOOK2MANIP(hooknum); | ||
| 423 | |||
| 424 | if (!skb_make_writable(skb, hdrlen + sizeof(*inside))) | ||
| 425 | return 0; | ||
| 426 | |||
| 427 | inside = (void *)skb->data + hdrlen; | ||
| 428 | |||
| 429 | /* We're actually going to mangle it beyond trivial checksum | ||
| 430 | adjustment, so make sure the current checksum is correct. */ | ||
| 431 | if (nf_ip_checksum(skb, hooknum, hdrlen, 0)) | ||
| 432 | return 0; | ||
| 433 | |||
| 434 | /* Must be RELATED */ | ||
| 435 | NF_CT_ASSERT(skb->nfctinfo == IP_CT_RELATED || | ||
| 436 | skb->nfctinfo == IP_CT_RELATED_REPLY); | ||
| 437 | |||
| 438 | /* Redirects on non-null nats must be dropped, else they'll | ||
| 439 | start talking to each other without our translation, and be | ||
| 440 | confused... --RR */ | ||
| 441 | if (inside->icmp.type == ICMP_REDIRECT) { | ||
| 442 | /* If NAT isn't finished, assume it and drop. */ | ||
| 443 | if ((ct->status & IPS_NAT_DONE_MASK) != IPS_NAT_DONE_MASK) | ||
| 444 | return 0; | ||
| 445 | |||
| 446 | if (ct->status & IPS_NAT_MASK) | ||
| 447 | return 0; | ||
| 448 | } | ||
| 449 | |||
| 450 | if (manip == IP_NAT_MANIP_SRC) | ||
| 451 | statusbit = IPS_SRC_NAT; | ||
| 452 | else | ||
| 453 | statusbit = IPS_DST_NAT; | ||
| 454 | |||
| 455 | /* Invert if this is reply dir. */ | ||
| 456 | if (dir == IP_CT_DIR_REPLY) | ||
| 457 | statusbit ^= IPS_NAT_MASK; | ||
| 458 | |||
| 459 | if (!(ct->status & statusbit)) | ||
| 460 | return 1; | ||
| 461 | |||
| 462 | pr_debug("icmp_reply_translation: translating error %p manip %u " | ||
| 463 | "dir %s\n", skb, manip, | ||
| 464 | dir == IP_CT_DIR_ORIGINAL ? "ORIG" : "REPLY"); | ||
| 465 | |||
| 466 | /* rcu_read_lock()ed by nf_hook_slow */ | ||
| 467 | l4proto = __nf_ct_l4proto_find(PF_INET, inside->ip.protocol); | ||
| 468 | |||
| 469 | if (!nf_ct_get_tuple(skb, hdrlen + sizeof(struct icmphdr), | ||
| 470 | (hdrlen + | ||
| 471 | sizeof(struct icmphdr) + inside->ip.ihl * 4), | ||
| 472 | (u_int16_t)AF_INET, inside->ip.protocol, | ||
| 473 | &inner, l3proto, l4proto)) | ||
| 474 | return 0; | ||
| 475 | |||
| 476 | /* Change inner back to look like incoming packet. We do the | ||
| 477 | opposite manip on this hook to normal, because it might not | ||
| 478 | pass all hooks (locally-generated ICMP). Consider incoming | ||
| 479 | packet: PREROUTING (DST manip), routing produces ICMP, goes | ||
| 480 | through POSTROUTING (which must correct the DST manip). */ | ||
| 481 | if (!manip_pkt(inside->ip.protocol, skb, hdrlen + sizeof(inside->icmp), | ||
| 482 | &ct->tuplehash[!dir].tuple, !manip)) | ||
| 483 | return 0; | ||
| 484 | |||
| 485 | if (skb->ip_summed != CHECKSUM_PARTIAL) { | ||
| 486 | /* Reloading "inside" here since manip_pkt inner. */ | ||
| 487 | inside = (void *)skb->data + hdrlen; | ||
| 488 | inside->icmp.checksum = 0; | ||
| 489 | inside->icmp.checksum = | ||
| 490 | csum_fold(skb_checksum(skb, hdrlen, | ||
| 491 | skb->len - hdrlen, 0)); | ||
| 492 | } | ||
| 493 | |||
| 494 | /* Change outer to look the reply to an incoming packet | ||
| 495 | * (proto 0 means don't invert per-proto part). */ | ||
| 496 | nf_ct_invert_tuplepr(&target, &ct->tuplehash[!dir].tuple); | ||
| 497 | if (!manip_pkt(0, skb, 0, &target, manip)) | ||
| 498 | return 0; | ||
| 499 | |||
| 500 | return 1; | ||
| 501 | } | ||
| 502 | EXPORT_SYMBOL_GPL(nf_nat_icmp_reply_translation); | ||
| 503 | |||
| 504 | /* Protocol registration. */ | ||
| 505 | int nf_nat_protocol_register(const struct nf_nat_protocol *proto) | ||
| 506 | { | ||
| 507 | int ret = 0; | ||
| 508 | |||
| 509 | spin_lock_bh(&nf_nat_lock); | ||
| 510 | if (rcu_dereference_protected( | ||
| 511 | nf_nat_protos[proto->protonum], | ||
| 512 | lockdep_is_held(&nf_nat_lock) | ||
| 513 | ) != &nf_nat_unknown_protocol) { | ||
| 514 | ret = -EBUSY; | ||
| 515 | goto out; | ||
| 516 | } | ||
| 517 | rcu_assign_pointer(nf_nat_protos[proto->protonum], proto); | ||
| 518 | out: | ||
| 519 | spin_unlock_bh(&nf_nat_lock); | ||
| 520 | return ret; | ||
| 521 | } | ||
| 522 | EXPORT_SYMBOL(nf_nat_protocol_register); | ||
| 523 | |||
| 524 | /* No one stores the protocol anywhere; simply delete it. */ | ||
| 525 | void nf_nat_protocol_unregister(const struct nf_nat_protocol *proto) | ||
| 526 | { | ||
| 527 | spin_lock_bh(&nf_nat_lock); | ||
| 528 | rcu_assign_pointer(nf_nat_protos[proto->protonum], | ||
| 529 | &nf_nat_unknown_protocol); | ||
| 530 | spin_unlock_bh(&nf_nat_lock); | ||
| 531 | synchronize_rcu(); | ||
| 532 | } | ||
| 533 | EXPORT_SYMBOL(nf_nat_protocol_unregister); | ||
| 534 | |||
| 535 | /* No one using conntrack by the time this called. */ | ||
| 536 | static void nf_nat_cleanup_conntrack(struct nf_conn *ct) | ||
| 537 | { | ||
| 538 | struct nf_conn_nat *nat = nf_ct_ext_find(ct, NF_CT_EXT_NAT); | ||
| 539 | |||
| 540 | if (nat == NULL || nat->ct == NULL) | ||
| 541 | return; | ||
| 542 | |||
| 543 | NF_CT_ASSERT(nat->ct->status & IPS_SRC_NAT_DONE); | ||
| 544 | |||
| 545 | spin_lock_bh(&nf_nat_lock); | ||
| 546 | hlist_del_rcu(&nat->bysource); | ||
| 547 | spin_unlock_bh(&nf_nat_lock); | ||
| 548 | } | ||
| 549 | |||
| 550 | static void nf_nat_move_storage(void *new, void *old) | ||
| 551 | { | ||
| 552 | struct nf_conn_nat *new_nat = new; | ||
| 553 | struct nf_conn_nat *old_nat = old; | ||
| 554 | struct nf_conn *ct = old_nat->ct; | ||
| 555 | |||
| 556 | if (!ct || !(ct->status & IPS_SRC_NAT_DONE)) | ||
| 557 | return; | ||
| 558 | |||
| 559 | spin_lock_bh(&nf_nat_lock); | ||
| 560 | hlist_replace_rcu(&old_nat->bysource, &new_nat->bysource); | ||
| 561 | spin_unlock_bh(&nf_nat_lock); | ||
| 562 | } | ||
| 563 | |||
| 564 | static struct nf_ct_ext_type nat_extend __read_mostly = { | ||
| 565 | .len = sizeof(struct nf_conn_nat), | ||
| 566 | .align = __alignof__(struct nf_conn_nat), | ||
| 567 | .destroy = nf_nat_cleanup_conntrack, | ||
| 568 | .move = nf_nat_move_storage, | ||
| 569 | .id = NF_CT_EXT_NAT, | ||
| 570 | .flags = NF_CT_EXT_F_PREALLOC, | ||
| 571 | }; | ||
| 572 | |||
| 573 | #if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE) | ||
| 574 | |||
| 575 | #include <linux/netfilter/nfnetlink.h> | ||
| 576 | #include <linux/netfilter/nfnetlink_conntrack.h> | ||
| 577 | |||
| 578 | static const struct nf_nat_protocol * | ||
| 579 | nf_nat_proto_find_get(u_int8_t protonum) | ||
| 580 | { | ||
| 581 | const struct nf_nat_protocol *p; | ||
| 582 | |||
| 583 | rcu_read_lock(); | ||
| 584 | p = __nf_nat_proto_find(protonum); | ||
| 585 | if (!try_module_get(p->me)) | ||
| 586 | p = &nf_nat_unknown_protocol; | ||
| 587 | rcu_read_unlock(); | ||
| 588 | |||
| 589 | return p; | ||
| 590 | } | ||
| 591 | |||
| 592 | static void | ||
| 593 | nf_nat_proto_put(const struct nf_nat_protocol *p) | ||
| 594 | { | ||
| 595 | module_put(p->me); | ||
| 596 | } | ||
| 597 | |||
| 598 | static const struct nla_policy protonat_nla_policy[CTA_PROTONAT_MAX+1] = { | ||
| 599 | [CTA_PROTONAT_PORT_MIN] = { .type = NLA_U16 }, | ||
| 600 | [CTA_PROTONAT_PORT_MAX] = { .type = NLA_U16 }, | ||
| 601 | }; | ||
| 602 | |||
| 603 | static int nfnetlink_parse_nat_proto(struct nlattr *attr, | ||
| 604 | const struct nf_conn *ct, | ||
| 605 | struct nf_nat_range *range) | ||
| 606 | { | ||
| 607 | struct nlattr *tb[CTA_PROTONAT_MAX+1]; | ||
| 608 | const struct nf_nat_protocol *npt; | ||
| 609 | int err; | ||
| 610 | |||
| 611 | err = nla_parse_nested(tb, CTA_PROTONAT_MAX, attr, protonat_nla_policy); | ||
| 612 | if (err < 0) | ||
| 613 | return err; | ||
| 614 | |||
| 615 | npt = nf_nat_proto_find_get(nf_ct_protonum(ct)); | ||
| 616 | if (npt->nlattr_to_range) | ||
| 617 | err = npt->nlattr_to_range(tb, range); | ||
| 618 | nf_nat_proto_put(npt); | ||
| 619 | return err; | ||
| 620 | } | ||
| 621 | |||
| 622 | static const struct nla_policy nat_nla_policy[CTA_NAT_MAX+1] = { | ||
| 623 | [CTA_NAT_MINIP] = { .type = NLA_U32 }, | ||
| 624 | [CTA_NAT_MAXIP] = { .type = NLA_U32 }, | ||
| 625 | }; | ||
| 626 | |||
| 627 | static int | ||
| 628 | nfnetlink_parse_nat(const struct nlattr *nat, | ||
| 629 | const struct nf_conn *ct, struct nf_nat_range *range) | ||
| 630 | { | ||
| 631 | struct nlattr *tb[CTA_NAT_MAX+1]; | ||
| 632 | int err; | ||
| 633 | |||
| 634 | memset(range, 0, sizeof(*range)); | ||
| 635 | |||
| 636 | err = nla_parse_nested(tb, CTA_NAT_MAX, nat, nat_nla_policy); | ||
| 637 | if (err < 0) | ||
| 638 | return err; | ||
| 639 | |||
| 640 | if (tb[CTA_NAT_MINIP]) | ||
| 641 | range->min_ip = nla_get_be32(tb[CTA_NAT_MINIP]); | ||
| 642 | |||
| 643 | if (!tb[CTA_NAT_MAXIP]) | ||
| 644 | range->max_ip = range->min_ip; | ||
| 645 | else | ||
| 646 | range->max_ip = nla_get_be32(tb[CTA_NAT_MAXIP]); | ||
| 647 | |||
| 648 | if (range->min_ip) | ||
| 649 | range->flags |= IP_NAT_RANGE_MAP_IPS; | ||
| 650 | |||
| 651 | if (!tb[CTA_NAT_PROTO]) | ||
| 652 | return 0; | ||
| 653 | |||
| 654 | err = nfnetlink_parse_nat_proto(tb[CTA_NAT_PROTO], ct, range); | ||
| 655 | if (err < 0) | ||
| 656 | return err; | ||
| 657 | |||
| 658 | return 0; | ||
| 659 | } | ||
| 660 | |||
| 661 | static int | ||
| 662 | nfnetlink_parse_nat_setup(struct nf_conn *ct, | ||
| 663 | enum nf_nat_manip_type manip, | ||
| 664 | const struct nlattr *attr) | ||
| 665 | { | ||
| 666 | struct nf_nat_range range; | ||
| 667 | |||
| 668 | if (nfnetlink_parse_nat(attr, ct, &range) < 0) | ||
| 669 | return -EINVAL; | ||
| 670 | if (nf_nat_initialized(ct, manip)) | ||
| 671 | return -EEXIST; | ||
| 672 | |||
| 673 | return nf_nat_setup_info(ct, &range, manip); | ||
| 674 | } | ||
| 675 | #else | ||
| 676 | static int | ||
| 677 | nfnetlink_parse_nat_setup(struct nf_conn *ct, | ||
| 678 | enum nf_nat_manip_type manip, | ||
| 679 | const struct nlattr *attr) | ||
| 680 | { | ||
| 681 | return -EOPNOTSUPP; | ||
| 682 | } | ||
| 683 | #endif | ||
| 684 | |||
| 685 | static int __net_init nf_nat_net_init(struct net *net) | ||
| 686 | { | ||
| 687 | /* Leave them the same for the moment. */ | ||
| 688 | net->ipv4.nat_htable_size = net->ct.htable_size; | ||
| 689 | net->ipv4.nat_bysource = nf_ct_alloc_hashtable(&net->ipv4.nat_htable_size, 0); | ||
| 690 | if (!net->ipv4.nat_bysource) | ||
| 691 | return -ENOMEM; | ||
| 692 | return 0; | ||
| 693 | } | ||
| 694 | |||
| 695 | /* Clear NAT section of all conntracks, in case we're loaded again. */ | ||
| 696 | static int clean_nat(struct nf_conn *i, void *data) | ||
| 697 | { | ||
| 698 | struct nf_conn_nat *nat = nfct_nat(i); | ||
| 699 | |||
| 700 | if (!nat) | ||
| 701 | return 0; | ||
| 702 | memset(nat, 0, sizeof(*nat)); | ||
| 703 | i->status &= ~(IPS_NAT_MASK | IPS_NAT_DONE_MASK | IPS_SEQ_ADJUST); | ||
| 704 | return 0; | ||
| 705 | } | ||
| 706 | |||
| 707 | static void __net_exit nf_nat_net_exit(struct net *net) | ||
| 708 | { | ||
| 709 | nf_ct_iterate_cleanup(net, &clean_nat, NULL); | ||
| 710 | synchronize_rcu(); | ||
| 711 | nf_ct_free_hashtable(net->ipv4.nat_bysource, net->ipv4.nat_htable_size); | ||
| 712 | } | ||
| 713 | |||
| 714 | static struct pernet_operations nf_nat_net_ops = { | ||
| 715 | .init = nf_nat_net_init, | ||
| 716 | .exit = nf_nat_net_exit, | ||
| 717 | }; | ||
| 718 | |||
| 719 | static int __init nf_nat_init(void) | ||
| 720 | { | ||
| 721 | size_t i; | ||
| 722 | int ret; | ||
| 723 | |||
| 724 | need_ipv4_conntrack(); | ||
| 725 | |||
| 726 | ret = nf_ct_extend_register(&nat_extend); | ||
| 727 | if (ret < 0) { | ||
| 728 | printk(KERN_ERR "nf_nat_core: Unable to register extension\n"); | ||
| 729 | return ret; | ||
| 730 | } | ||
| 731 | |||
| 732 | ret = register_pernet_subsys(&nf_nat_net_ops); | ||
| 733 | if (ret < 0) | ||
| 734 | goto cleanup_extend; | ||
| 735 | |||
| 736 | /* Sew in builtin protocols. */ | ||
| 737 | spin_lock_bh(&nf_nat_lock); | ||
| 738 | for (i = 0; i < MAX_IP_NAT_PROTO; i++) | ||
| 739 | rcu_assign_pointer(nf_nat_protos[i], &nf_nat_unknown_protocol); | ||
| 740 | rcu_assign_pointer(nf_nat_protos[IPPROTO_TCP], &nf_nat_protocol_tcp); | ||
| 741 | rcu_assign_pointer(nf_nat_protos[IPPROTO_UDP], &nf_nat_protocol_udp); | ||
| 742 | rcu_assign_pointer(nf_nat_protos[IPPROTO_ICMP], &nf_nat_protocol_icmp); | ||
| 743 | spin_unlock_bh(&nf_nat_lock); | ||
| 744 | |||
| 745 | /* Initialize fake conntrack so that NAT will skip it */ | ||
| 746 | nf_ct_untracked_status_or(IPS_NAT_DONE_MASK); | ||
| 747 | |||
| 748 | l3proto = nf_ct_l3proto_find_get((u_int16_t)AF_INET); | ||
| 749 | |||
| 750 | BUG_ON(nf_nat_seq_adjust_hook != NULL); | ||
| 751 | rcu_assign_pointer(nf_nat_seq_adjust_hook, nf_nat_seq_adjust); | ||
| 752 | BUG_ON(nfnetlink_parse_nat_setup_hook != NULL); | ||
| 753 | rcu_assign_pointer(nfnetlink_parse_nat_setup_hook, | ||
| 754 | nfnetlink_parse_nat_setup); | ||
| 755 | BUG_ON(nf_ct_nat_offset != NULL); | ||
| 756 | rcu_assign_pointer(nf_ct_nat_offset, nf_nat_get_offset); | ||
| 757 | return 0; | ||
| 758 | |||
| 759 | cleanup_extend: | ||
| 760 | nf_ct_extend_unregister(&nat_extend); | ||
| 761 | return ret; | ||
| 762 | } | ||
| 763 | |||
| 764 | static void __exit nf_nat_cleanup(void) | ||
| 765 | { | ||
| 766 | unregister_pernet_subsys(&nf_nat_net_ops); | ||
| 767 | nf_ct_l3proto_put(l3proto); | ||
| 768 | nf_ct_extend_unregister(&nat_extend); | ||
| 769 | rcu_assign_pointer(nf_nat_seq_adjust_hook, NULL); | ||
| 770 | rcu_assign_pointer(nfnetlink_parse_nat_setup_hook, NULL); | ||
| 771 | rcu_assign_pointer(nf_ct_nat_offset, NULL); | ||
| 772 | synchronize_net(); | ||
| 773 | } | ||
| 774 | |||
| 775 | MODULE_LICENSE("GPL"); | ||
| 776 | MODULE_ALIAS("nf-nat-ipv4"); | ||
| 777 | |||
| 778 | module_init(nf_nat_init); | ||
| 779 | module_exit(nf_nat_cleanup); | ||
diff --git a/net/ipv4/netfilter/nf_nat_ftp.c b/net/ipv4/netfilter/nf_nat_ftp.c new file mode 100644 index 00000000000..dc73abb3fe2 --- /dev/null +++ b/net/ipv4/netfilter/nf_nat_ftp.c | |||
| @@ -0,0 +1,137 @@ | |||
| 1 | /* FTP extension for TCP NAT alteration. */ | ||
| 2 | |||
| 3 | /* (C) 1999-2001 Paul `Rusty' Russell | ||
| 4 | * (C) 2002-2006 Netfilter Core Team <coreteam@netfilter.org> | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU General Public License version 2 as | ||
| 8 | * published by the Free Software Foundation. | ||
| 9 | */ | ||
| 10 | |||
| 11 | #include <linux/module.h> | ||
| 12 | #include <linux/moduleparam.h> | ||
| 13 | #include <linux/ip.h> | ||
| 14 | #include <linux/tcp.h> | ||
| 15 | #include <linux/netfilter_ipv4.h> | ||
| 16 | #include <net/netfilter/nf_nat.h> | ||
| 17 | #include <net/netfilter/nf_nat_helper.h> | ||
| 18 | #include <net/netfilter/nf_nat_rule.h> | ||
| 19 | #include <net/netfilter/nf_conntrack_helper.h> | ||
| 20 | #include <net/netfilter/nf_conntrack_expect.h> | ||
| 21 | #include <linux/netfilter/nf_conntrack_ftp.h> | ||
| 22 | |||
| 23 | MODULE_LICENSE("GPL"); | ||
| 24 | MODULE_AUTHOR("Rusty Russell <rusty@rustcorp.com.au>"); | ||
| 25 | MODULE_DESCRIPTION("ftp NAT helper"); | ||
| 26 | MODULE_ALIAS("ip_nat_ftp"); | ||
| 27 | |||
| 28 | /* FIXME: Time out? --RR */ | ||
| 29 | |||
| 30 | static int nf_nat_ftp_fmt_cmd(enum nf_ct_ftp_type type, | ||
| 31 | char *buffer, size_t buflen, | ||
| 32 | __be32 addr, u16 port) | ||
| 33 | { | ||
| 34 | switch (type) { | ||
| 35 | case NF_CT_FTP_PORT: | ||
| 36 | case NF_CT_FTP_PASV: | ||
| 37 | return snprintf(buffer, buflen, "%u,%u,%u,%u,%u,%u", | ||
| 38 | ((unsigned char *)&addr)[0], | ||
| 39 | ((unsigned char *)&addr)[1], | ||
| 40 | ((unsigned char *)&addr)[2], | ||
| 41 | ((unsigned char *)&addr)[3], | ||
| 42 | port >> 8, | ||
| 43 | port & 0xFF); | ||
| 44 | case NF_CT_FTP_EPRT: | ||
| 45 | return snprintf(buffer, buflen, "|1|%pI4|%u|", &addr, port); | ||
| 46 | case NF_CT_FTP_EPSV: | ||
| 47 | return snprintf(buffer, buflen, "|||%u|", port); | ||
| 48 | } | ||
| 49 | |||
| 50 | return 0; | ||
| 51 | } | ||
| 52 | |||
| 53 | /* So, this packet has hit the connection tracking matching code. | ||
| 54 | Mangle it, and change the expectation to match the new version. */ | ||
| 55 | static unsigned int nf_nat_ftp(struct sk_buff *skb, | ||
| 56 | enum ip_conntrack_info ctinfo, | ||
| 57 | enum nf_ct_ftp_type type, | ||
| 58 | unsigned int matchoff, | ||
| 59 | unsigned int matchlen, | ||
| 60 | struct nf_conntrack_expect *exp) | ||
| 61 | { | ||
| 62 | __be32 newip; | ||
| 63 | u_int16_t port; | ||
| 64 | int dir = CTINFO2DIR(ctinfo); | ||
| 65 | struct nf_conn *ct = exp->master; | ||
| 66 | char buffer[sizeof("|1|255.255.255.255|65535|")]; | ||
| 67 | unsigned int buflen; | ||
| 68 | |||
| 69 | pr_debug("FTP_NAT: type %i, off %u len %u\n", type, matchoff, matchlen); | ||
| 70 | |||
| 71 | /* Connection will come from wherever this packet goes, hence !dir */ | ||
| 72 | newip = ct->tuplehash[!dir].tuple.dst.u3.ip; | ||
| 73 | exp->saved_proto.tcp.port = exp->tuple.dst.u.tcp.port; | ||
| 74 | exp->dir = !dir; | ||
| 75 | |||
| 76 | /* When you see the packet, we need to NAT it the same as the | ||
| 77 | * this one. */ | ||
| 78 | exp->expectfn = nf_nat_follow_master; | ||
| 79 | |||
| 80 | /* Try to get same port: if not, try to change it. */ | ||
| 81 | for (port = ntohs(exp->saved_proto.tcp.port); port != 0; port++) { | ||
| 82 | int ret; | ||
| 83 | |||
| 84 | exp->tuple.dst.u.tcp.port = htons(port); | ||
| 85 | ret = nf_ct_expect_related(exp); | ||
| 86 | if (ret == 0) | ||
| 87 | break; | ||
| 88 | else if (ret != -EBUSY) { | ||
| 89 | port = 0; | ||
| 90 | break; | ||
| 91 | } | ||
| 92 | } | ||
| 93 | |||
| 94 | if (port == 0) | ||
| 95 | return NF_DROP; | ||
| 96 | |||
| 97 | buflen = nf_nat_ftp_fmt_cmd(type, buffer, sizeof(buffer), newip, port); | ||
| 98 | if (!buflen) | ||
| 99 | goto out; | ||
| 100 | |||
| 101 | pr_debug("calling nf_nat_mangle_tcp_packet\n"); | ||
| 102 | |||
| 103 | if (!nf_nat_mangle_tcp_packet(skb, ct, ctinfo, matchoff, | ||
| 104 | matchlen, buffer, buflen)) | ||
| 105 | goto out; | ||
| 106 | |||
| 107 | return NF_ACCEPT; | ||
| 108 | |||
| 109 | out: | ||
| 110 | nf_ct_unexpect_related(exp); | ||
| 111 | return NF_DROP; | ||
| 112 | } | ||
| 113 | |||
| 114 | static void __exit nf_nat_ftp_fini(void) | ||
| 115 | { | ||
| 116 | rcu_assign_pointer(nf_nat_ftp_hook, NULL); | ||
| 117 | synchronize_rcu(); | ||
| 118 | } | ||
| 119 | |||
| 120 | static int __init nf_nat_ftp_init(void) | ||
| 121 | { | ||
| 122 | BUG_ON(nf_nat_ftp_hook != NULL); | ||
| 123 | rcu_assign_pointer(nf_nat_ftp_hook, nf_nat_ftp); | ||
| 124 | return 0; | ||
| 125 | } | ||
| 126 | |||
| 127 | /* Prior to 2.6.11, we had a ports param. No longer, but don't break users. */ | ||
| 128 | static int warn_set(const char *val, struct kernel_param *kp) | ||
| 129 | { | ||
| 130 | printk(KERN_INFO KBUILD_MODNAME | ||
| 131 | ": kernel >= 2.6.10 only uses 'ports' for conntrack modules\n"); | ||
| 132 | return 0; | ||
| 133 | } | ||
| 134 | module_param_call(ports, warn_set, NULL, NULL, 0); | ||
| 135 | |||
| 136 | module_init(nf_nat_ftp_init); | ||
| 137 | module_exit(nf_nat_ftp_fini); | ||
diff --git a/net/ipv4/netfilter/nf_nat_helper.c b/net/ipv4/netfilter/nf_nat_helper.c new file mode 100644 index 00000000000..ebc5f8894f9 --- /dev/null +++ b/net/ipv4/netfilter/nf_nat_helper.c | |||
| @@ -0,0 +1,451 @@ | |||
| 1 | /* ip_nat_helper.c - generic support functions for NAT helpers | ||
| 2 | * | ||
| 3 | * (C) 2000-2002 Harald Welte <laforge@netfilter.org> | ||
| 4 | * (C) 2003-2006 Netfilter Core Team <coreteam@netfilter.org> | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU General Public License version 2 as | ||
| 8 | * published by the Free Software Foundation. | ||
| 9 | */ | ||
| 10 | #include <linux/module.h> | ||
| 11 | #include <linux/gfp.h> | ||
| 12 | #include <linux/kmod.h> | ||
| 13 | #include <linux/types.h> | ||
| 14 | #include <linux/timer.h> | ||
| 15 | #include <linux/skbuff.h> | ||
| 16 | #include <linux/tcp.h> | ||
| 17 | #include <linux/udp.h> | ||
| 18 | #include <net/checksum.h> | ||
| 19 | #include <net/tcp.h> | ||
| 20 | #include <net/route.h> | ||
| 21 | |||
| 22 | #include <linux/netfilter_ipv4.h> | ||
| 23 | #include <net/netfilter/nf_conntrack.h> | ||
| 24 | #include <net/netfilter/nf_conntrack_helper.h> | ||
| 25 | #include <net/netfilter/nf_conntrack_ecache.h> | ||
| 26 | #include <net/netfilter/nf_conntrack_expect.h> | ||
| 27 | #include <net/netfilter/nf_nat.h> | ||
| 28 | #include <net/netfilter/nf_nat_protocol.h> | ||
| 29 | #include <net/netfilter/nf_nat_core.h> | ||
| 30 | #include <net/netfilter/nf_nat_helper.h> | ||
| 31 | |||
| 32 | #define DUMP_OFFSET(x) \ | ||
| 33 | pr_debug("offset_before=%d, offset_after=%d, correction_pos=%u\n", \ | ||
| 34 | x->offset_before, x->offset_after, x->correction_pos); | ||
| 35 | |||
| 36 | static DEFINE_SPINLOCK(nf_nat_seqofs_lock); | ||
| 37 | |||
| 38 | /* Setup TCP sequence correction given this change at this sequence */ | ||
| 39 | static inline void | ||
| 40 | adjust_tcp_sequence(u32 seq, | ||
| 41 | int sizediff, | ||
| 42 | struct nf_conn *ct, | ||
| 43 | enum ip_conntrack_info ctinfo) | ||
| 44 | { | ||
| 45 | enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); | ||
| 46 | struct nf_conn_nat *nat = nfct_nat(ct); | ||
| 47 | struct nf_nat_seq *this_way = &nat->seq[dir]; | ||
| 48 | |||
| 49 | pr_debug("adjust_tcp_sequence: seq = %u, sizediff = %d\n", | ||
| 50 | seq, sizediff); | ||
| 51 | |||
| 52 | pr_debug("adjust_tcp_sequence: Seq_offset before: "); | ||
| 53 | DUMP_OFFSET(this_way); | ||
| 54 | |||
| 55 | spin_lock_bh(&nf_nat_seqofs_lock); | ||
| 56 | |||
| 57 | /* SYN adjust. If it's uninitialized, or this is after last | ||
| 58 | * correction, record it: we don't handle more than one | ||
| 59 | * adjustment in the window, but do deal with common case of a | ||
| 60 | * retransmit */ | ||
| 61 | if (this_way->offset_before == this_way->offset_after || | ||
| 62 | before(this_way->correction_pos, seq)) { | ||
| 63 | this_way->correction_pos = seq; | ||
| 64 | this_way->offset_before = this_way->offset_after; | ||
| 65 | this_way->offset_after += sizediff; | ||
| 66 | } | ||
| 67 | spin_unlock_bh(&nf_nat_seqofs_lock); | ||
| 68 | |||
| 69 | pr_debug("adjust_tcp_sequence: Seq_offset after: "); | ||
| 70 | DUMP_OFFSET(this_way); | ||
| 71 | } | ||
| 72 | |||
| 73 | /* Get the offset value, for conntrack */ | ||
| 74 | s16 nf_nat_get_offset(const struct nf_conn *ct, | ||
| 75 | enum ip_conntrack_dir dir, | ||
| 76 | u32 seq) | ||
| 77 | { | ||
| 78 | struct nf_conn_nat *nat = nfct_nat(ct); | ||
| 79 | struct nf_nat_seq *this_way; | ||
| 80 | s16 offset; | ||
| 81 | |||
| 82 | if (!nat) | ||
| 83 | return 0; | ||
| 84 | |||
| 85 | this_way = &nat->seq[dir]; | ||
| 86 | spin_lock_bh(&nf_nat_seqofs_lock); | ||
| 87 | offset = after(seq, this_way->correction_pos) | ||
| 88 | ? this_way->offset_after : this_way->offset_before; | ||
| 89 | spin_unlock_bh(&nf_nat_seqofs_lock); | ||
| 90 | |||
| 91 | return offset; | ||
| 92 | } | ||
| 93 | EXPORT_SYMBOL_GPL(nf_nat_get_offset); | ||
| 94 | |||
| 95 | /* Frobs data inside this packet, which is linear. */ | ||
| 96 | static void mangle_contents(struct sk_buff *skb, | ||
| 97 | unsigned int dataoff, | ||
| 98 | unsigned int match_offset, | ||
| 99 | unsigned int match_len, | ||
| 100 | const char *rep_buffer, | ||
| 101 | unsigned int rep_len) | ||
| 102 | { | ||
| 103 | unsigned char *data; | ||
| 104 | |||
| 105 | BUG_ON(skb_is_nonlinear(skb)); | ||
| 106 | data = skb_network_header(skb) + dataoff; | ||
| 107 | |||
| 108 | /* move post-replacement */ | ||
| 109 | memmove(data + match_offset + rep_len, | ||
| 110 | data + match_offset + match_len, | ||
| 111 | skb->tail - (skb->network_header + dataoff + | ||
| 112 | match_offset + match_len)); | ||
| 113 | |||
| 114 | /* insert data from buffer */ | ||
| 115 | memcpy(data + match_offset, rep_buffer, rep_len); | ||
| 116 | |||
| 117 | /* update skb info */ | ||
| 118 | if (rep_len > match_len) { | ||
| 119 | pr_debug("nf_nat_mangle_packet: Extending packet by " | ||
| 120 | "%u from %u bytes\n", rep_len - match_len, skb->len); | ||
| 121 | skb_put(skb, rep_len - match_len); | ||
| 122 | } else { | ||
| 123 | pr_debug("nf_nat_mangle_packet: Shrinking packet from " | ||
| 124 | "%u from %u bytes\n", match_len - rep_len, skb->len); | ||
| 125 | __skb_trim(skb, skb->len + rep_len - match_len); | ||
| 126 | } | ||
| 127 | |||
| 128 | /* fix IP hdr checksum information */ | ||
| 129 | ip_hdr(skb)->tot_len = htons(skb->len); | ||
| 130 | ip_send_check(ip_hdr(skb)); | ||
| 131 | } | ||
| 132 | |||
| 133 | /* Unusual, but possible case. */ | ||
| 134 | static int enlarge_skb(struct sk_buff *skb, unsigned int extra) | ||
| 135 | { | ||
| 136 | if (skb->len + extra > 65535) | ||
| 137 | return 0; | ||
| 138 | |||
| 139 | if (pskb_expand_head(skb, 0, extra - skb_tailroom(skb), GFP_ATOMIC)) | ||
| 140 | return 0; | ||
| 141 | |||
| 142 | return 1; | ||
| 143 | } | ||
| 144 | |||
| 145 | void nf_nat_set_seq_adjust(struct nf_conn *ct, enum ip_conntrack_info ctinfo, | ||
| 146 | __be32 seq, s16 off) | ||
| 147 | { | ||
| 148 | if (!off) | ||
| 149 | return; | ||
| 150 | set_bit(IPS_SEQ_ADJUST_BIT, &ct->status); | ||
| 151 | adjust_tcp_sequence(ntohl(seq), off, ct, ctinfo); | ||
| 152 | nf_conntrack_event_cache(IPCT_NATSEQADJ, ct); | ||
| 153 | } | ||
| 154 | EXPORT_SYMBOL_GPL(nf_nat_set_seq_adjust); | ||
| 155 | |||
| 156 | static void nf_nat_csum(struct sk_buff *skb, const struct iphdr *iph, void *data, | ||
| 157 | int datalen, __sum16 *check, int oldlen) | ||
| 158 | { | ||
| 159 | struct rtable *rt = skb_rtable(skb); | ||
| 160 | |||
| 161 | if (skb->ip_summed != CHECKSUM_PARTIAL) { | ||
| 162 | if (!(rt->rt_flags & RTCF_LOCAL) && | ||
| 163 | (!skb->dev || skb->dev->features & NETIF_F_V4_CSUM)) { | ||
| 164 | skb->ip_summed = CHECKSUM_PARTIAL; | ||
| 165 | skb->csum_start = skb_headroom(skb) + | ||
| 166 | skb_network_offset(skb) + | ||
| 167 | iph->ihl * 4; | ||
| 168 | skb->csum_offset = (void *)check - data; | ||
| 169 | *check = ~csum_tcpudp_magic(iph->saddr, iph->daddr, | ||
| 170 | datalen, iph->protocol, 0); | ||
| 171 | } else { | ||
| 172 | *check = 0; | ||
| 173 | *check = csum_tcpudp_magic(iph->saddr, iph->daddr, | ||
| 174 | datalen, iph->protocol, | ||
| 175 | csum_partial(data, datalen, | ||
| 176 | 0)); | ||
| 177 | if (iph->protocol == IPPROTO_UDP && !*check) | ||
| 178 | *check = CSUM_MANGLED_0; | ||
| 179 | } | ||
| 180 | } else | ||
| 181 | inet_proto_csum_replace2(check, skb, | ||
| 182 | htons(oldlen), htons(datalen), 1); | ||
| 183 | } | ||
| 184 | |||
| 185 | /* Generic function for mangling variable-length address changes inside | ||
| 186 | * NATed TCP connections (like the PORT XXX,XXX,XXX,XXX,XXX,XXX | ||
| 187 | * command in FTP). | ||
| 188 | * | ||
| 189 | * Takes care about all the nasty sequence number changes, checksumming, | ||
| 190 | * skb enlargement, ... | ||
| 191 | * | ||
| 192 | * */ | ||
| 193 | int __nf_nat_mangle_tcp_packet(struct sk_buff *skb, | ||
| 194 | struct nf_conn *ct, | ||
| 195 | enum ip_conntrack_info ctinfo, | ||
| 196 | unsigned int match_offset, | ||
| 197 | unsigned int match_len, | ||
| 198 | const char *rep_buffer, | ||
| 199 | unsigned int rep_len, bool adjust) | ||
| 200 | { | ||
| 201 | struct iphdr *iph; | ||
| 202 | struct tcphdr *tcph; | ||
| 203 | int oldlen, datalen; | ||
| 204 | |||
| 205 | if (!skb_make_writable(skb, skb->len)) | ||
| 206 | return 0; | ||
| 207 | |||
| 208 | if (rep_len > match_len && | ||
| 209 | rep_len - match_len > skb_tailroom(skb) && | ||
| 210 | !enlarge_skb(skb, rep_len - match_len)) | ||
| 211 | return 0; | ||
| 212 | |||
| 213 | SKB_LINEAR_ASSERT(skb); | ||
| 214 | |||
| 215 | iph = ip_hdr(skb); | ||
| 216 | tcph = (void *)iph + iph->ihl*4; | ||
| 217 | |||
| 218 | oldlen = skb->len - iph->ihl*4; | ||
| 219 | mangle_contents(skb, iph->ihl*4 + tcph->doff*4, | ||
| 220 | match_offset, match_len, rep_buffer, rep_len); | ||
| 221 | |||
| 222 | datalen = skb->len - iph->ihl*4; | ||
| 223 | nf_nat_csum(skb, iph, tcph, datalen, &tcph->check, oldlen); | ||
| 224 | |||
| 225 | if (adjust && rep_len != match_len) | ||
| 226 | nf_nat_set_seq_adjust(ct, ctinfo, tcph->seq, | ||
| 227 | (int)rep_len - (int)match_len); | ||
| 228 | |||
| 229 | return 1; | ||
| 230 | } | ||
| 231 | EXPORT_SYMBOL(__nf_nat_mangle_tcp_packet); | ||
| 232 | |||
| 233 | /* Generic function for mangling variable-length address changes inside | ||
| 234 | * NATed UDP connections (like the CONNECT DATA XXXXX MESG XXXXX INDEX XXXXX | ||
| 235 | * command in the Amanda protocol) | ||
| 236 | * | ||
| 237 | * Takes care about all the nasty sequence number changes, checksumming, | ||
| 238 | * skb enlargement, ... | ||
| 239 | * | ||
| 240 | * XXX - This function could be merged with nf_nat_mangle_tcp_packet which | ||
| 241 | * should be fairly easy to do. | ||
| 242 | */ | ||
| 243 | int | ||
| 244 | nf_nat_mangle_udp_packet(struct sk_buff *skb, | ||
| 245 | struct nf_conn *ct, | ||
| 246 | enum ip_conntrack_info ctinfo, | ||
| 247 | unsigned int match_offset, | ||
| 248 | unsigned int match_len, | ||
| 249 | const char *rep_buffer, | ||
| 250 | unsigned int rep_len) | ||
| 251 | { | ||
| 252 | struct iphdr *iph; | ||
| 253 | struct udphdr *udph; | ||
| 254 | int datalen, oldlen; | ||
| 255 | |||
| 256 | /* UDP helpers might accidentally mangle the wrong packet */ | ||
| 257 | iph = ip_hdr(skb); | ||
| 258 | if (skb->len < iph->ihl*4 + sizeof(*udph) + | ||
| 259 | match_offset + match_len) | ||
| 260 | return 0; | ||
| 261 | |||
| 262 | if (!skb_make_writable(skb, skb->len)) | ||
| 263 | return 0; | ||
| 264 | |||
| 265 | if (rep_len > match_len && | ||
| 266 | rep_len - match_len > skb_tailroom(skb) && | ||
| 267 | !enlarge_skb(skb, rep_len - match_len)) | ||
| 268 | return 0; | ||
| 269 | |||
| 270 | iph = ip_hdr(skb); | ||
| 271 | udph = (void *)iph + iph->ihl*4; | ||
| 272 | |||
| 273 | oldlen = skb->len - iph->ihl*4; | ||
| 274 | mangle_contents(skb, iph->ihl*4 + sizeof(*udph), | ||
| 275 | match_offset, match_len, rep_buffer, rep_len); | ||
| 276 | |||
| 277 | /* update the length of the UDP packet */ | ||
| 278 | datalen = skb->len - iph->ihl*4; | ||
| 279 | udph->len = htons(datalen); | ||
| 280 | |||
| 281 | /* fix udp checksum if udp checksum was previously calculated */ | ||
| 282 | if (!udph->check && skb->ip_summed != CHECKSUM_PARTIAL) | ||
| 283 | return 1; | ||
| 284 | |||
| 285 | nf_nat_csum(skb, iph, udph, datalen, &udph->check, oldlen); | ||
| 286 | |||
| 287 | return 1; | ||
| 288 | } | ||
| 289 | EXPORT_SYMBOL(nf_nat_mangle_udp_packet); | ||
| 290 | |||
| 291 | /* Adjust one found SACK option including checksum correction */ | ||
| 292 | static void | ||
| 293 | sack_adjust(struct sk_buff *skb, | ||
| 294 | struct tcphdr *tcph, | ||
| 295 | unsigned int sackoff, | ||
| 296 | unsigned int sackend, | ||
| 297 | struct nf_nat_seq *natseq) | ||
| 298 | { | ||
| 299 | while (sackoff < sackend) { | ||
| 300 | struct tcp_sack_block_wire *sack; | ||
| 301 | __be32 new_start_seq, new_end_seq; | ||
| 302 | |||
| 303 | sack = (void *)skb->data + sackoff; | ||
| 304 | if (after(ntohl(sack->start_seq) - natseq->offset_before, | ||
| 305 | natseq->correction_pos)) | ||
| 306 | new_start_seq = htonl(ntohl(sack->start_seq) | ||
| 307 | - natseq->offset_after); | ||
| 308 | else | ||
| 309 | new_start_seq = htonl(ntohl(sack->start_seq) | ||
| 310 | - natseq->offset_before); | ||
| 311 | |||
| 312 | if (after(ntohl(sack->end_seq) - natseq->offset_before, | ||
| 313 | natseq->correction_pos)) | ||
| 314 | new_end_seq = htonl(ntohl(sack->end_seq) | ||
| 315 | - natseq->offset_after); | ||
| 316 | else | ||
| 317 | new_end_seq = htonl(ntohl(sack->end_seq) | ||
| 318 | - natseq->offset_before); | ||
| 319 | |||
| 320 | pr_debug("sack_adjust: start_seq: %d->%d, end_seq: %d->%d\n", | ||
| 321 | ntohl(sack->start_seq), new_start_seq, | ||
| 322 | ntohl(sack->end_seq), new_end_seq); | ||
| 323 | |||
| 324 | inet_proto_csum_replace4(&tcph->check, skb, | ||
| 325 | sack->start_seq, new_start_seq, 0); | ||
| 326 | inet_proto_csum_replace4(&tcph->check, skb, | ||
| 327 | sack->end_seq, new_end_seq, 0); | ||
| 328 | sack->start_seq = new_start_seq; | ||
| 329 | sack->end_seq = new_end_seq; | ||
| 330 | sackoff += sizeof(*sack); | ||
| 331 | } | ||
| 332 | } | ||
| 333 | |||
| 334 | /* TCP SACK sequence number adjustment */ | ||
| 335 | static inline unsigned int | ||
| 336 | nf_nat_sack_adjust(struct sk_buff *skb, | ||
| 337 | struct tcphdr *tcph, | ||
| 338 | struct nf_conn *ct, | ||
| 339 | enum ip_conntrack_info ctinfo) | ||
| 340 | { | ||
| 341 | unsigned int dir, optoff, optend; | ||
| 342 | struct nf_conn_nat *nat = nfct_nat(ct); | ||
| 343 | |||
| 344 | optoff = ip_hdrlen(skb) + sizeof(struct tcphdr); | ||
| 345 | optend = ip_hdrlen(skb) + tcph->doff * 4; | ||
| 346 | |||
| 347 | if (!skb_make_writable(skb, optend)) | ||
| 348 | return 0; | ||
| 349 | |||
| 350 | dir = CTINFO2DIR(ctinfo); | ||
| 351 | |||
| 352 | while (optoff < optend) { | ||
| 353 | /* Usually: option, length. */ | ||
| 354 | unsigned char *op = skb->data + optoff; | ||
| 355 | |||
| 356 | switch (op[0]) { | ||
| 357 | case TCPOPT_EOL: | ||
| 358 | return 1; | ||
| 359 | case TCPOPT_NOP: | ||
| 360 | optoff++; | ||
| 361 | continue; | ||
| 362 | default: | ||
| 363 | /* no partial options */ | ||
| 364 | if (optoff + 1 == optend || | ||
| 365 | optoff + op[1] > optend || | ||
| 366 | op[1] < 2) | ||
| 367 | return 0; | ||
| 368 | if (op[0] == TCPOPT_SACK && | ||
| 369 | op[1] >= 2+TCPOLEN_SACK_PERBLOCK && | ||
| 370 | ((op[1] - 2) % TCPOLEN_SACK_PERBLOCK) == 0) | ||
| 371 | sack_adjust(skb, tcph, optoff+2, | ||
| 372 | optoff+op[1], &nat->seq[!dir]); | ||
| 373 | optoff += op[1]; | ||
| 374 | } | ||
| 375 | } | ||
| 376 | return 1; | ||
| 377 | } | ||
| 378 | |||
| 379 | /* TCP sequence number adjustment. Returns 1 on success, 0 on failure */ | ||
| 380 | int | ||
| 381 | nf_nat_seq_adjust(struct sk_buff *skb, | ||
| 382 | struct nf_conn *ct, | ||
| 383 | enum ip_conntrack_info ctinfo) | ||
| 384 | { | ||
| 385 | struct tcphdr *tcph; | ||
| 386 | int dir; | ||
| 387 | __be32 newseq, newack; | ||
| 388 | s16 seqoff, ackoff; | ||
| 389 | struct nf_conn_nat *nat = nfct_nat(ct); | ||
| 390 | struct nf_nat_seq *this_way, *other_way; | ||
| 391 | |||
| 392 | dir = CTINFO2DIR(ctinfo); | ||
| 393 | |||
| 394 | this_way = &nat->seq[dir]; | ||
| 395 | other_way = &nat->seq[!dir]; | ||
| 396 | |||
| 397 | if (!skb_make_writable(skb, ip_hdrlen(skb) + sizeof(*tcph))) | ||
| 398 | return 0; | ||
| 399 | |||
| 400 | tcph = (void *)skb->data + ip_hdrlen(skb); | ||
| 401 | if (after(ntohl(tcph->seq), this_way->correction_pos)) | ||
| 402 | seqoff = this_way->offset_after; | ||
| 403 | else | ||
| 404 | seqoff = this_way->offset_before; | ||
| 405 | |||
| 406 | if (after(ntohl(tcph->ack_seq) - other_way->offset_before, | ||
| 407 | other_way->correction_pos)) | ||
| 408 | ackoff = other_way->offset_after; | ||
| 409 | else | ||
| 410 | ackoff = other_way->offset_before; | ||
| 411 | |||
| 412 | newseq = htonl(ntohl(tcph->seq) + seqoff); | ||
| 413 | newack = htonl(ntohl(tcph->ack_seq) - ackoff); | ||
| 414 | |||
| 415 | inet_proto_csum_replace4(&tcph->check, skb, tcph->seq, newseq, 0); | ||
| 416 | inet_proto_csum_replace4(&tcph->check, skb, tcph->ack_seq, newack, 0); | ||
| 417 | |||
| 418 | pr_debug("Adjusting sequence number from %u->%u, ack from %u->%u\n", | ||
| 419 | ntohl(tcph->seq), ntohl(newseq), ntohl(tcph->ack_seq), | ||
| 420 | ntohl(newack)); | ||
| 421 | |||
| 422 | tcph->seq = newseq; | ||
| 423 | tcph->ack_seq = newack; | ||
| 424 | |||
| 425 | return nf_nat_sack_adjust(skb, tcph, ct, ctinfo); | ||
| 426 | } | ||
| 427 | |||
| 428 | /* Setup NAT on this expected conntrack so it follows master. */ | ||
| 429 | /* If we fail to get a free NAT slot, we'll get dropped on confirm */ | ||
| 430 | void nf_nat_follow_master(struct nf_conn *ct, | ||
| 431 | struct nf_conntrack_expect *exp) | ||
| 432 | { | ||
| 433 | struct nf_nat_range range; | ||
| 434 | |||
| 435 | /* This must be a fresh one. */ | ||
| 436 | BUG_ON(ct->status & IPS_NAT_DONE_MASK); | ||
| 437 | |||
| 438 | /* Change src to where master sends to */ | ||
| 439 | range.flags = IP_NAT_RANGE_MAP_IPS; | ||
| 440 | range.min_ip = range.max_ip | ||
| 441 | = ct->master->tuplehash[!exp->dir].tuple.dst.u3.ip; | ||
| 442 | nf_nat_setup_info(ct, &range, IP_NAT_MANIP_SRC); | ||
| 443 | |||
| 444 | /* For DST manip, map port here to where it's expected. */ | ||
| 445 | range.flags = (IP_NAT_RANGE_MAP_IPS | IP_NAT_RANGE_PROTO_SPECIFIED); | ||
| 446 | range.min = range.max = exp->saved_proto; | ||
| 447 | range.min_ip = range.max_ip | ||
| 448 | = ct->master->tuplehash[!exp->dir].tuple.src.u3.ip; | ||
| 449 | nf_nat_setup_info(ct, &range, IP_NAT_MANIP_DST); | ||
| 450 | } | ||
| 451 | EXPORT_SYMBOL(nf_nat_follow_master); | ||
diff --git a/net/ipv4/netfilter/nf_nat_irc.c b/net/ipv4/netfilter/nf_nat_irc.c new file mode 100644 index 00000000000..535e1a80235 --- /dev/null +++ b/net/ipv4/netfilter/nf_nat_irc.c | |||
| @@ -0,0 +1,99 @@ | |||
| 1 | /* IRC extension for TCP NAT alteration. | ||
| 2 | * | ||
| 3 | * (C) 2000-2001 by Harald Welte <laforge@gnumonks.org> | ||
| 4 | * (C) 2004 Rusty Russell <rusty@rustcorp.com.au> IBM Corporation | ||
| 5 | * based on a copy of RR's ip_nat_ftp.c | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or | ||
| 8 | * modify it under the terms of the GNU General Public License | ||
| 9 | * as published by the Free Software Foundation; either version | ||
| 10 | * 2 of the License, or (at your option) any later version. | ||
| 11 | */ | ||
| 12 | |||
| 13 | #include <linux/module.h> | ||
| 14 | #include <linux/moduleparam.h> | ||
| 15 | #include <linux/tcp.h> | ||
| 16 | #include <linux/kernel.h> | ||
| 17 | |||
| 18 | #include <net/netfilter/nf_nat.h> | ||
| 19 | #include <net/netfilter/nf_nat_helper.h> | ||
| 20 | #include <net/netfilter/nf_nat_rule.h> | ||
| 21 | #include <net/netfilter/nf_conntrack_helper.h> | ||
| 22 | #include <net/netfilter/nf_conntrack_expect.h> | ||
| 23 | #include <linux/netfilter/nf_conntrack_irc.h> | ||
| 24 | |||
| 25 | MODULE_AUTHOR("Harald Welte <laforge@gnumonks.org>"); | ||
| 26 | MODULE_DESCRIPTION("IRC (DCC) NAT helper"); | ||
| 27 | MODULE_LICENSE("GPL"); | ||
| 28 | MODULE_ALIAS("ip_nat_irc"); | ||
| 29 | |||
| 30 | static unsigned int help(struct sk_buff *skb, | ||
| 31 | enum ip_conntrack_info ctinfo, | ||
| 32 | unsigned int matchoff, | ||
| 33 | unsigned int matchlen, | ||
| 34 | struct nf_conntrack_expect *exp) | ||
| 35 | { | ||
| 36 | char buffer[sizeof("4294967296 65635")]; | ||
| 37 | u_int32_t ip; | ||
| 38 | u_int16_t port; | ||
| 39 | unsigned int ret; | ||
| 40 | |||
| 41 | /* Reply comes from server. */ | ||
| 42 | exp->saved_proto.tcp.port = exp->tuple.dst.u.tcp.port; | ||
| 43 | exp->dir = IP_CT_DIR_REPLY; | ||
| 44 | exp->expectfn = nf_nat_follow_master; | ||
| 45 | |||
| 46 | /* Try to get same port: if not, try to change it. */ | ||
| 47 | for (port = ntohs(exp->saved_proto.tcp.port); port != 0; port++) { | ||
| 48 | int ret; | ||
| 49 | |||
| 50 | exp->tuple.dst.u.tcp.port = htons(port); | ||
| 51 | ret = nf_ct_expect_related(exp); | ||
| 52 | if (ret == 0) | ||
| 53 | break; | ||
| 54 | else if (ret != -EBUSY) { | ||
| 55 | port = 0; | ||
| 56 | break; | ||
| 57 | } | ||
| 58 | } | ||
| 59 | |||
| 60 | if (port == 0) | ||
| 61 | return NF_DROP; | ||
| 62 | |||
| 63 | ip = ntohl(exp->master->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u3.ip); | ||
| 64 | sprintf(buffer, "%u %u", ip, port); | ||
| 65 | pr_debug("nf_nat_irc: inserting '%s' == %pI4, port %u\n", | ||
| 66 | buffer, &ip, port); | ||
| 67 | |||
| 68 | ret = nf_nat_mangle_tcp_packet(skb, exp->master, ctinfo, | ||
| 69 | matchoff, matchlen, buffer, | ||
| 70 | strlen(buffer)); | ||
| 71 | if (ret != NF_ACCEPT) | ||
| 72 | nf_ct_unexpect_related(exp); | ||
| 73 | return ret; | ||
| 74 | } | ||
| 75 | |||
| 76 | static void __exit nf_nat_irc_fini(void) | ||
| 77 | { | ||
| 78 | rcu_assign_pointer(nf_nat_irc_hook, NULL); | ||
| 79 | synchronize_rcu(); | ||
| 80 | } | ||
| 81 | |||
| 82 | static int __init nf_nat_irc_init(void) | ||
| 83 | { | ||
| 84 | BUG_ON(nf_nat_irc_hook != NULL); | ||
| 85 | rcu_assign_pointer(nf_nat_irc_hook, help); | ||
| 86 | return 0; | ||
| 87 | } | ||
| 88 | |||
| 89 | /* Prior to 2.6.11, we had a ports param. No longer, but don't break users. */ | ||
| 90 | static int warn_set(const char *val, struct kernel_param *kp) | ||
| 91 | { | ||
| 92 | printk(KERN_INFO KBUILD_MODNAME | ||
| 93 | ": kernel >= 2.6.10 only uses 'ports' for conntrack modules\n"); | ||
| 94 | return 0; | ||
| 95 | } | ||
| 96 | module_param_call(ports, warn_set, NULL, NULL, 0); | ||
| 97 | |||
| 98 | module_init(nf_nat_irc_init); | ||
| 99 | module_exit(nf_nat_irc_fini); | ||
diff --git a/net/ipv4/netfilter/nf_nat_proto_common.c b/net/ipv4/netfilter/nf_nat_proto_common.c new file mode 100644 index 00000000000..f52d41ea069 --- /dev/null +++ b/net/ipv4/netfilter/nf_nat_proto_common.c | |||
| @@ -0,0 +1,125 @@ | |||
| 1 | /* (C) 1999-2001 Paul `Rusty' Russell | ||
| 2 | * (C) 2002-2006 Netfilter Core Team <coreteam@netfilter.org> | ||
| 3 | * (C) 2008 Patrick McHardy <kaber@trash.net> | ||
| 4 | * | ||
| 5 | * This program is free software; you can redistribute it and/or modify | ||
| 6 | * it under the terms of the GNU General Public License version 2 as | ||
| 7 | * published by the Free Software Foundation. | ||
| 8 | */ | ||
| 9 | |||
| 10 | #include <linux/types.h> | ||
| 11 | #include <linux/random.h> | ||
| 12 | #include <linux/ip.h> | ||
| 13 | |||
| 14 | #include <linux/netfilter.h> | ||
| 15 | #include <net/secure_seq.h> | ||
| 16 | #include <net/netfilter/nf_nat.h> | ||
| 17 | #include <net/netfilter/nf_nat_core.h> | ||
| 18 | #include <net/netfilter/nf_nat_rule.h> | ||
| 19 | #include <net/netfilter/nf_nat_protocol.h> | ||
| 20 | |||
| 21 | bool nf_nat_proto_in_range(const struct nf_conntrack_tuple *tuple, | ||
| 22 | enum nf_nat_manip_type maniptype, | ||
| 23 | const union nf_conntrack_man_proto *min, | ||
| 24 | const union nf_conntrack_man_proto *max) | ||
| 25 | { | ||
| 26 | __be16 port; | ||
| 27 | |||
| 28 | if (maniptype == IP_NAT_MANIP_SRC) | ||
| 29 | port = tuple->src.u.all; | ||
| 30 | else | ||
| 31 | port = tuple->dst.u.all; | ||
| 32 | |||
| 33 | return ntohs(port) >= ntohs(min->all) && | ||
| 34 | ntohs(port) <= ntohs(max->all); | ||
| 35 | } | ||
| 36 | EXPORT_SYMBOL_GPL(nf_nat_proto_in_range); | ||
| 37 | |||
| 38 | void nf_nat_proto_unique_tuple(struct nf_conntrack_tuple *tuple, | ||
| 39 | const struct nf_nat_range *range, | ||
| 40 | enum nf_nat_manip_type maniptype, | ||
| 41 | const struct nf_conn *ct, | ||
| 42 | u_int16_t *rover) | ||
| 43 | { | ||
| 44 | unsigned int range_size, min, i; | ||
| 45 | __be16 *portptr; | ||
| 46 | u_int16_t off; | ||
| 47 | |||
| 48 | if (maniptype == IP_NAT_MANIP_SRC) | ||
| 49 | portptr = &tuple->src.u.all; | ||
| 50 | else | ||
| 51 | portptr = &tuple->dst.u.all; | ||
| 52 | |||
| 53 | /* If no range specified... */ | ||
| 54 | if (!(range->flags & IP_NAT_RANGE_PROTO_SPECIFIED)) { | ||
| 55 | /* If it's dst rewrite, can't change port */ | ||
| 56 | if (maniptype == IP_NAT_MANIP_DST) | ||
| 57 | return; | ||
| 58 | |||
| 59 | if (ntohs(*portptr) < 1024) { | ||
| 60 | /* Loose convention: >> 512 is credential passing */ | ||
| 61 | if (ntohs(*portptr) < 512) { | ||
| 62 | min = 1; | ||
| 63 | range_size = 511 - min + 1; | ||
| 64 | } else { | ||
| 65 | min = 600; | ||
| 66 | range_size = 1023 - min + 1; | ||
| 67 | } | ||
| 68 | } else { | ||
| 69 | min = 1024; | ||
| 70 | range_size = 65535 - 1024 + 1; | ||
| 71 | } | ||
| 72 | } else { | ||
| 73 | min = ntohs(range->min.all); | ||
| 74 | range_size = ntohs(range->max.all) - min + 1; | ||
| 75 | } | ||
| 76 | |||
| 77 | if (range->flags & IP_NAT_RANGE_PROTO_RANDOM) | ||
| 78 | off = secure_ipv4_port_ephemeral(tuple->src.u3.ip, tuple->dst.u3.ip, | ||
| 79 | maniptype == IP_NAT_MANIP_SRC | ||
| 80 | ? tuple->dst.u.all | ||
| 81 | : tuple->src.u.all); | ||
| 82 | else | ||
| 83 | off = *rover; | ||
| 84 | |||
| 85 | for (i = 0; ; ++off) { | ||
| 86 | *portptr = htons(min + off % range_size); | ||
| 87 | if (++i != range_size && nf_nat_used_tuple(tuple, ct)) | ||
| 88 | continue; | ||
| 89 | if (!(range->flags & IP_NAT_RANGE_PROTO_RANDOM)) | ||
| 90 | *rover = off; | ||
| 91 | return; | ||
| 92 | } | ||
| 93 | return; | ||
| 94 | } | ||
| 95 | EXPORT_SYMBOL_GPL(nf_nat_proto_unique_tuple); | ||
| 96 | |||
| 97 | #if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE) | ||
| 98 | int nf_nat_proto_range_to_nlattr(struct sk_buff *skb, | ||
| 99 | const struct nf_nat_range *range) | ||
| 100 | { | ||
| 101 | NLA_PUT_BE16(skb, CTA_PROTONAT_PORT_MIN, range->min.all); | ||
| 102 | NLA_PUT_BE16(skb, CTA_PROTONAT_PORT_MAX, range->max.all); | ||
| 103 | return 0; | ||
| 104 | |||
| 105 | nla_put_failure: | ||
| 106 | return -1; | ||
| 107 | } | ||
| 108 | EXPORT_SYMBOL_GPL(nf_nat_proto_nlattr_to_range); | ||
| 109 | |||
| 110 | int nf_nat_proto_nlattr_to_range(struct nlattr *tb[], | ||
| 111 | struct nf_nat_range *range) | ||
| 112 | { | ||
| 113 | if (tb[CTA_PROTONAT_PORT_MIN]) { | ||
| 114 | range->min.all = nla_get_be16(tb[CTA_PROTONAT_PORT_MIN]); | ||
| 115 | range->max.all = range->min.tcp.port; | ||
| 116 | range->flags |= IP_NAT_RANGE_PROTO_SPECIFIED; | ||
| 117 | } | ||
| 118 | if (tb[CTA_PROTONAT_PORT_MAX]) { | ||
| 119 | range->max.all = nla_get_be16(tb[CTA_PROTONAT_PORT_MAX]); | ||
| 120 | range->flags |= IP_NAT_RANGE_PROTO_SPECIFIED; | ||
| 121 | } | ||
| 122 | return 0; | ||
| 123 | } | ||
| 124 | EXPORT_SYMBOL_GPL(nf_nat_proto_range_to_nlattr); | ||
| 125 | #endif | ||
diff --git a/net/ipv4/netfilter/nf_nat_proto_dccp.c b/net/ipv4/netfilter/nf_nat_proto_dccp.c new file mode 100644 index 00000000000..570faf2667b --- /dev/null +++ b/net/ipv4/netfilter/nf_nat_proto_dccp.c | |||
| @@ -0,0 +1,108 @@ | |||
| 1 | /* | ||
| 2 | * DCCP NAT protocol helper | ||
| 3 | * | ||
| 4 | * Copyright (c) 2005, 2006. 2008 Patrick McHardy <kaber@trash.net> | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU General Public License version 2 as | ||
| 8 | * published by the Free Software Foundation. | ||
| 9 | * | ||
| 10 | */ | ||
| 11 | |||
| 12 | #include <linux/kernel.h> | ||
| 13 | #include <linux/module.h> | ||
| 14 | #include <linux/init.h> | ||
| 15 | #include <linux/skbuff.h> | ||
| 16 | #include <linux/ip.h> | ||
| 17 | #include <linux/dccp.h> | ||
| 18 | |||
| 19 | #include <net/netfilter/nf_conntrack.h> | ||
| 20 | #include <net/netfilter/nf_nat.h> | ||
| 21 | #include <net/netfilter/nf_nat_protocol.h> | ||
| 22 | |||
| 23 | static u_int16_t dccp_port_rover; | ||
| 24 | |||
| 25 | static void | ||
| 26 | dccp_unique_tuple(struct nf_conntrack_tuple *tuple, | ||
| 27 | const struct nf_nat_range *range, | ||
| 28 | enum nf_nat_manip_type maniptype, | ||
| 29 | const struct nf_conn *ct) | ||
| 30 | { | ||
| 31 | nf_nat_proto_unique_tuple(tuple, range, maniptype, ct, | ||
| 32 | &dccp_port_rover); | ||
| 33 | } | ||
| 34 | |||
| 35 | static bool | ||
| 36 | dccp_manip_pkt(struct sk_buff *skb, | ||
| 37 | unsigned int iphdroff, | ||
| 38 | const struct nf_conntrack_tuple *tuple, | ||
| 39 | enum nf_nat_manip_type maniptype) | ||
| 40 | { | ||
| 41 | const struct iphdr *iph = (const void *)(skb->data + iphdroff); | ||
| 42 | struct dccp_hdr *hdr; | ||
| 43 | unsigned int hdroff = iphdroff + iph->ihl * 4; | ||
| 44 | __be32 oldip, newip; | ||
| 45 | __be16 *portptr, oldport, newport; | ||
| 46 | int hdrsize = 8; /* DCCP connection tracking guarantees this much */ | ||
| 47 | |||
| 48 | if (skb->len >= hdroff + sizeof(struct dccp_hdr)) | ||
| 49 | hdrsize = sizeof(struct dccp_hdr); | ||
| 50 | |||
| 51 | if (!skb_make_writable(skb, hdroff + hdrsize)) | ||
| 52 | return false; | ||
| 53 | |||
| 54 | iph = (struct iphdr *)(skb->data + iphdroff); | ||
| 55 | hdr = (struct dccp_hdr *)(skb->data + hdroff); | ||
| 56 | |||
| 57 | if (maniptype == IP_NAT_MANIP_SRC) { | ||
| 58 | oldip = iph->saddr; | ||
| 59 | newip = tuple->src.u3.ip; | ||
| 60 | newport = tuple->src.u.dccp.port; | ||
| 61 | portptr = &hdr->dccph_sport; | ||
| 62 | } else { | ||
| 63 | oldip = iph->daddr; | ||
| 64 | newip = tuple->dst.u3.ip; | ||
| 65 | newport = tuple->dst.u.dccp.port; | ||
| 66 | portptr = &hdr->dccph_dport; | ||
| 67 | } | ||
| 68 | |||
| 69 | oldport = *portptr; | ||
| 70 | *portptr = newport; | ||
| 71 | |||
| 72 | if (hdrsize < sizeof(*hdr)) | ||
| 73 | return true; | ||
| 74 | |||
| 75 | inet_proto_csum_replace4(&hdr->dccph_checksum, skb, oldip, newip, 1); | ||
| 76 | inet_proto_csum_replace2(&hdr->dccph_checksum, skb, oldport, newport, | ||
| 77 | 0); | ||
| 78 | return true; | ||
| 79 | } | ||
| 80 | |||
| 81 | static const struct nf_nat_protocol nf_nat_protocol_dccp = { | ||
| 82 | .protonum = IPPROTO_DCCP, | ||
| 83 | .me = THIS_MODULE, | ||
| 84 | .manip_pkt = dccp_manip_pkt, | ||
| 85 | .in_range = nf_nat_proto_in_range, | ||
| 86 | .unique_tuple = dccp_unique_tuple, | ||
| 87 | #if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE) | ||
| 88 | .range_to_nlattr = nf_nat_proto_range_to_nlattr, | ||
| 89 | .nlattr_to_range = nf_nat_proto_nlattr_to_range, | ||
| 90 | #endif | ||
| 91 | }; | ||
| 92 | |||
| 93 | static int __init nf_nat_proto_dccp_init(void) | ||
| 94 | { | ||
| 95 | return nf_nat_protocol_register(&nf_nat_protocol_dccp); | ||
| 96 | } | ||
| 97 | |||
| 98 | static void __exit nf_nat_proto_dccp_fini(void) | ||
| 99 | { | ||
| 100 | nf_nat_protocol_unregister(&nf_nat_protocol_dccp); | ||
| 101 | } | ||
| 102 | |||
| 103 | module_init(nf_nat_proto_dccp_init); | ||
| 104 | module_exit(nf_nat_proto_dccp_fini); | ||
| 105 | |||
| 106 | MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>"); | ||
| 107 | MODULE_DESCRIPTION("DCCP NAT protocol helper"); | ||
| 108 | MODULE_LICENSE("GPL"); | ||
diff --git a/net/ipv4/netfilter/nf_nat_proto_sctp.c b/net/ipv4/netfilter/nf_nat_proto_sctp.c new file mode 100644 index 00000000000..756331d4266 --- /dev/null +++ b/net/ipv4/netfilter/nf_nat_proto_sctp.c | |||
| @@ -0,0 +1,97 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2008 Patrick McHardy <kaber@trash.net> | ||
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify | ||
| 5 | * it under the terms of the GNU General Public License version 2 as | ||
| 6 | * published by the Free Software Foundation. | ||
| 7 | */ | ||
| 8 | |||
| 9 | #include <linux/types.h> | ||
| 10 | #include <linux/init.h> | ||
| 11 | #include <linux/ip.h> | ||
| 12 | #include <linux/sctp.h> | ||
| 13 | #include <net/sctp/checksum.h> | ||
| 14 | |||
| 15 | #include <net/netfilter/nf_nat_protocol.h> | ||
| 16 | |||
| 17 | static u_int16_t nf_sctp_port_rover; | ||
| 18 | |||
| 19 | static void | ||
| 20 | sctp_unique_tuple(struct nf_conntrack_tuple *tuple, | ||
| 21 | const struct nf_nat_range *range, | ||
| 22 | enum nf_nat_manip_type maniptype, | ||
| 23 | const struct nf_conn *ct) | ||
| 24 | { | ||
| 25 | nf_nat_proto_unique_tuple(tuple, range, maniptype, ct, | ||
| 26 | &nf_sctp_port_rover); | ||
| 27 | } | ||
| 28 | |||
| 29 | static bool | ||
| 30 | sctp_manip_pkt(struct sk_buff *skb, | ||
| 31 | unsigned int iphdroff, | ||
| 32 | const struct nf_conntrack_tuple *tuple, | ||
| 33 | enum nf_nat_manip_type maniptype) | ||
| 34 | { | ||
| 35 | const struct iphdr *iph = (struct iphdr *)(skb->data + iphdroff); | ||
| 36 | struct sk_buff *frag; | ||
| 37 | sctp_sctphdr_t *hdr; | ||
| 38 | unsigned int hdroff = iphdroff + iph->ihl*4; | ||
| 39 | __be32 oldip, newip; | ||
| 40 | __be32 crc32; | ||
| 41 | |||
| 42 | if (!skb_make_writable(skb, hdroff + sizeof(*hdr))) | ||
| 43 | return false; | ||
| 44 | |||
| 45 | iph = (struct iphdr *)(skb->data + iphdroff); | ||
| 46 | hdr = (struct sctphdr *)(skb->data + hdroff); | ||
| 47 | |||
| 48 | if (maniptype == IP_NAT_MANIP_SRC) { | ||
| 49 | /* Get rid of src ip and src pt */ | ||
| 50 | oldip = iph->saddr; | ||
| 51 | newip = tuple->src.u3.ip; | ||
| 52 | hdr->source = tuple->src.u.sctp.port; | ||
| 53 | } else { | ||
| 54 | /* Get rid of dst ip and dst pt */ | ||
| 55 | oldip = iph->daddr; | ||
| 56 | newip = tuple->dst.u3.ip; | ||
| 57 | hdr->dest = tuple->dst.u.sctp.port; | ||
| 58 | } | ||
| 59 | |||
| 60 | crc32 = sctp_start_cksum((u8 *)hdr, skb_headlen(skb) - hdroff); | ||
| 61 | skb_walk_frags(skb, frag) | ||
| 62 | crc32 = sctp_update_cksum((u8 *)frag->data, skb_headlen(frag), | ||
| 63 | crc32); | ||
| 64 | crc32 = sctp_end_cksum(crc32); | ||
| 65 | hdr->checksum = crc32; | ||
| 66 | |||
| 67 | return true; | ||
| 68 | } | ||
| 69 | |||
| 70 | static const struct nf_nat_protocol nf_nat_protocol_sctp = { | ||
| 71 | .protonum = IPPROTO_SCTP, | ||
| 72 | .me = THIS_MODULE, | ||
| 73 | .manip_pkt = sctp_manip_pkt, | ||
| 74 | .in_range = nf_nat_proto_in_range, | ||
| 75 | .unique_tuple = sctp_unique_tuple, | ||
| 76 | #if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE) | ||
| 77 | .range_to_nlattr = nf_nat_proto_range_to_nlattr, | ||
| 78 | .nlattr_to_range = nf_nat_proto_nlattr_to_range, | ||
| 79 | #endif | ||
| 80 | }; | ||
| 81 | |||
| 82 | static int __init nf_nat_proto_sctp_init(void) | ||
| 83 | { | ||
| 84 | return nf_nat_protocol_register(&nf_nat_protocol_sctp); | ||
| 85 | } | ||
| 86 | |||
| 87 | static void __exit nf_nat_proto_sctp_exit(void) | ||
| 88 | { | ||
| 89 | nf_nat_protocol_unregister(&nf_nat_protocol_sctp); | ||
| 90 | } | ||
| 91 | |||
| 92 | module_init(nf_nat_proto_sctp_init); | ||
| 93 | module_exit(nf_nat_proto_sctp_exit); | ||
| 94 | |||
| 95 | MODULE_LICENSE("GPL"); | ||
| 96 | MODULE_DESCRIPTION("SCTP NAT protocol helper"); | ||
| 97 | MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>"); | ||
diff --git a/net/ipv4/netfilter/nf_nat_proto_tcp.c b/net/ipv4/netfilter/nf_nat_proto_tcp.c new file mode 100644 index 00000000000..aa460a595d5 --- /dev/null +++ b/net/ipv4/netfilter/nf_nat_proto_tcp.c | |||
| @@ -0,0 +1,92 @@ | |||
| 1 | /* (C) 1999-2001 Paul `Rusty' Russell | ||
| 2 | * (C) 2002-2006 Netfilter Core Team <coreteam@netfilter.org> | ||
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify | ||
| 5 | * it under the terms of the GNU General Public License version 2 as | ||
| 6 | * published by the Free Software Foundation. | ||
| 7 | */ | ||
| 8 | |||
| 9 | #include <linux/types.h> | ||
| 10 | #include <linux/init.h> | ||
| 11 | #include <linux/ip.h> | ||
| 12 | #include <linux/tcp.h> | ||
| 13 | |||
| 14 | #include <linux/netfilter.h> | ||
| 15 | #include <linux/netfilter/nfnetlink_conntrack.h> | ||
| 16 | #include <net/netfilter/nf_nat.h> | ||
| 17 | #include <net/netfilter/nf_nat_rule.h> | ||
| 18 | #include <net/netfilter/nf_nat_protocol.h> | ||
| 19 | #include <net/netfilter/nf_nat_core.h> | ||
| 20 | |||
| 21 | static u_int16_t tcp_port_rover; | ||
| 22 | |||
| 23 | static void | ||
| 24 | tcp_unique_tuple(struct nf_conntrack_tuple *tuple, | ||
| 25 | const struct nf_nat_range *range, | ||
| 26 | enum nf_nat_manip_type maniptype, | ||
| 27 | const struct nf_conn *ct) | ||
| 28 | { | ||
| 29 | nf_nat_proto_unique_tuple(tuple, range, maniptype, ct, &tcp_port_rover); | ||
| 30 | } | ||
| 31 | |||
| 32 | static bool | ||
| 33 | tcp_manip_pkt(struct sk_buff *skb, | ||
| 34 | unsigned int iphdroff, | ||
| 35 | const struct nf_conntrack_tuple *tuple, | ||
| 36 | enum nf_nat_manip_type maniptype) | ||
| 37 | { | ||
| 38 | const struct iphdr *iph = (struct iphdr *)(skb->data + iphdroff); | ||
| 39 | struct tcphdr *hdr; | ||
| 40 | unsigned int hdroff = iphdroff + iph->ihl*4; | ||
| 41 | __be32 oldip, newip; | ||
| 42 | __be16 *portptr, newport, oldport; | ||
| 43 | int hdrsize = 8; /* TCP connection tracking guarantees this much */ | ||
| 44 | |||
| 45 | /* this could be a inner header returned in icmp packet; in such | ||
| 46 | cases we cannot update the checksum field since it is outside of | ||
| 47 | the 8 bytes of transport layer headers we are guaranteed */ | ||
| 48 | if (skb->len >= hdroff + sizeof(struct tcphdr)) | ||
| 49 | hdrsize = sizeof(struct tcphdr); | ||
| 50 | |||
| 51 | if (!skb_make_writable(skb, hdroff + hdrsize)) | ||
| 52 | return false; | ||
| 53 | |||
| 54 | iph = (struct iphdr *)(skb->data + iphdroff); | ||
| 55 | hdr = (struct tcphdr *)(skb->data + hdroff); | ||
| 56 | |||
| 57 | if (maniptype == IP_NAT_MANIP_SRC) { | ||
| 58 | /* Get rid of src ip and src pt */ | ||
| 59 | oldip = iph->saddr; | ||
| 60 | newip = tuple->src.u3.ip; | ||
| 61 | newport = tuple->src.u.tcp.port; | ||
| 62 | portptr = &hdr->source; | ||
| 63 | } else { | ||
| 64 | /* Get rid of dst ip and dst pt */ | ||
| 65 | oldip = iph->daddr; | ||
| 66 | newip = tuple->dst.u3.ip; | ||
| 67 | newport = tuple->dst.u.tcp.port; | ||
| 68 | portptr = &hdr->dest; | ||
| 69 | } | ||
| 70 | |||
| 71 | oldport = *portptr; | ||
| 72 | *portptr = newport; | ||
| 73 | |||
| 74 | if (hdrsize < sizeof(*hdr)) | ||
| 75 | return true; | ||
| 76 | |||
| 77 | inet_proto_csum_replace4(&hdr->check, skb, oldip, newip, 1); | ||
| 78 | inet_proto_csum_replace2(&hdr->check, skb, oldport, newport, 0); | ||
| 79 | return true; | ||
| 80 | } | ||
| 81 | |||
| 82 | const struct nf_nat_protocol nf_nat_protocol_tcp = { | ||
| 83 | .protonum = IPPROTO_TCP, | ||
| 84 | .me = THIS_MODULE, | ||
| 85 | .manip_pkt = tcp_manip_pkt, | ||
| 86 | .in_range = nf_nat_proto_in_range, | ||
| 87 | .unique_tuple = tcp_unique_tuple, | ||
| 88 | #if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE) | ||
| 89 | .range_to_nlattr = nf_nat_proto_range_to_nlattr, | ||
| 90 | .nlattr_to_range = nf_nat_proto_nlattr_to_range, | ||
| 91 | #endif | ||
| 92 | }; | ||
diff --git a/net/ipv4/netfilter/nf_nat_proto_udp.c b/net/ipv4/netfilter/nf_nat_proto_udp.c new file mode 100644 index 00000000000..dfe65c7e292 --- /dev/null +++ b/net/ipv4/netfilter/nf_nat_proto_udp.c | |||
| @@ -0,0 +1,83 @@ | |||
| 1 | /* (C) 1999-2001 Paul `Rusty' Russell | ||
| 2 | * (C) 2002-2006 Netfilter Core Team <coreteam@netfilter.org> | ||
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify | ||
| 5 | * it under the terms of the GNU General Public License version 2 as | ||
| 6 | * published by the Free Software Foundation. | ||
| 7 | */ | ||
| 8 | |||
| 9 | #include <linux/types.h> | ||
| 10 | #include <linux/init.h> | ||
| 11 | #include <linux/ip.h> | ||
| 12 | #include <linux/udp.h> | ||
| 13 | |||
| 14 | #include <linux/netfilter.h> | ||
| 15 | #include <net/netfilter/nf_nat.h> | ||
| 16 | #include <net/netfilter/nf_nat_core.h> | ||
| 17 | #include <net/netfilter/nf_nat_rule.h> | ||
| 18 | #include <net/netfilter/nf_nat_protocol.h> | ||
| 19 | |||
| 20 | static u_int16_t udp_port_rover; | ||
| 21 | |||
| 22 | static void | ||
| 23 | udp_unique_tuple(struct nf_conntrack_tuple *tuple, | ||
| 24 | const struct nf_nat_range *range, | ||
| 25 | enum nf_nat_manip_type maniptype, | ||
| 26 | const struct nf_conn *ct) | ||
| 27 | { | ||
| 28 | nf_nat_proto_unique_tuple(tuple, range, maniptype, ct, &udp_port_rover); | ||
| 29 | } | ||
| 30 | |||
| 31 | static bool | ||
| 32 | udp_manip_pkt(struct sk_buff *skb, | ||
| 33 | unsigned int iphdroff, | ||
| 34 | const struct nf_conntrack_tuple *tuple, | ||
| 35 | enum nf_nat_manip_type maniptype) | ||
| 36 | { | ||
| 37 | const struct iphdr *iph = (struct iphdr *)(skb->data + iphdroff); | ||
| 38 | struct udphdr *hdr; | ||
| 39 | unsigned int hdroff = iphdroff + iph->ihl*4; | ||
| 40 | __be32 oldip, newip; | ||
| 41 | __be16 *portptr, newport; | ||
| 42 | |||
| 43 | if (!skb_make_writable(skb, hdroff + sizeof(*hdr))) | ||
| 44 | return false; | ||
| 45 | |||
| 46 | iph = (struct iphdr *)(skb->data + iphdroff); | ||
| 47 | hdr = (struct udphdr *)(skb->data + hdroff); | ||
| 48 | |||
| 49 | if (maniptype == IP_NAT_MANIP_SRC) { | ||
| 50 | /* Get rid of src ip and src pt */ | ||
| 51 | oldip = iph->saddr; | ||
| 52 | newip = tuple->src.u3.ip; | ||
| 53 | newport = tuple->src.u.udp.port; | ||
| 54 | portptr = &hdr->source; | ||
| 55 | } else { | ||
| 56 | /* Get rid of dst ip and dst pt */ | ||
| 57 | oldip = iph->daddr; | ||
| 58 | newip = tuple->dst.u3.ip; | ||
| 59 | newport = tuple->dst.u.udp.port; | ||
| 60 | portptr = &hdr->dest; | ||
| 61 | } | ||
| 62 | if (hdr->check || skb->ip_summed == CHECKSUM_PARTIAL) { | ||
| 63 | inet_proto_csum_replace4(&hdr->check, skb, oldip, newip, 1); | ||
| 64 | inet_proto_csum_replace2(&hdr->check, skb, *portptr, newport, | ||
| 65 | 0); | ||
| 66 | if (!hdr->check) | ||
| 67 | hdr->check = CSUM_MANGLED_0; | ||
| 68 | } | ||
| 69 | *portptr = newport; | ||
| 70 | return true; | ||
| 71 | } | ||
| 72 | |||
| 73 | const struct nf_nat_protocol nf_nat_protocol_udp = { | ||
| 74 | .protonum = IPPROTO_UDP, | ||
| 75 | .me = THIS_MODULE, | ||
| 76 | .manip_pkt = udp_manip_pkt, | ||
| 77 | .in_range = nf_nat_proto_in_range, | ||
| 78 | .unique_tuple = udp_unique_tuple, | ||
| 79 | #if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE) | ||
| 80 | .range_to_nlattr = nf_nat_proto_range_to_nlattr, | ||
| 81 | .nlattr_to_range = nf_nat_proto_nlattr_to_range, | ||
| 82 | #endif | ||
| 83 | }; | ||
diff --git a/net/ipv4/netfilter/nf_nat_proto_udplite.c b/net/ipv4/netfilter/nf_nat_proto_udplite.c new file mode 100644 index 00000000000..3cc8c8af39e --- /dev/null +++ b/net/ipv4/netfilter/nf_nat_proto_udplite.c | |||
| @@ -0,0 +1,99 @@ | |||
| 1 | /* (C) 1999-2001 Paul `Rusty' Russell | ||
| 2 | * (C) 2002-2006 Netfilter Core Team <coreteam@netfilter.org> | ||
| 3 | * (C) 2008 Patrick McHardy <kaber@trash.net> | ||
| 4 | * | ||
| 5 | * This program is free software; you can redistribute it and/or modify | ||
| 6 | * it under the terms of the GNU General Public License version 2 as | ||
| 7 | * published by the Free Software Foundation. | ||
| 8 | */ | ||
| 9 | |||
| 10 | #include <linux/types.h> | ||
| 11 | #include <linux/init.h> | ||
| 12 | #include <linux/ip.h> | ||
| 13 | #include <linux/udp.h> | ||
| 14 | |||
| 15 | #include <linux/netfilter.h> | ||
| 16 | #include <net/netfilter/nf_nat.h> | ||
| 17 | #include <net/netfilter/nf_nat_protocol.h> | ||
| 18 | |||
| 19 | static u_int16_t udplite_port_rover; | ||
| 20 | |||
| 21 | static void | ||
| 22 | udplite_unique_tuple(struct nf_conntrack_tuple *tuple, | ||
| 23 | const struct nf_nat_range *range, | ||
| 24 | enum nf_nat_manip_type maniptype, | ||
| 25 | const struct nf_conn *ct) | ||
| 26 | { | ||
| 27 | nf_nat_proto_unique_tuple(tuple, range, maniptype, ct, | ||
| 28 | &udplite_port_rover); | ||
| 29 | } | ||
| 30 | |||
| 31 | static bool | ||
| 32 | udplite_manip_pkt(struct sk_buff *skb, | ||
| 33 | unsigned int iphdroff, | ||
| 34 | const struct nf_conntrack_tuple *tuple, | ||
| 35 | enum nf_nat_manip_type maniptype) | ||
| 36 | { | ||
| 37 | const struct iphdr *iph = (struct iphdr *)(skb->data + iphdroff); | ||
| 38 | struct udphdr *hdr; | ||
| 39 | unsigned int hdroff = iphdroff + iph->ihl*4; | ||
| 40 | __be32 oldip, newip; | ||
| 41 | __be16 *portptr, newport; | ||
| 42 | |||
| 43 | if (!skb_make_writable(skb, hdroff + sizeof(*hdr))) | ||
| 44 | return false; | ||
| 45 | |||
| 46 | iph = (struct iphdr *)(skb->data + iphdroff); | ||
| 47 | hdr = (struct udphdr *)(skb->data + hdroff); | ||
| 48 | |||
| 49 | if (maniptype == IP_NAT_MANIP_SRC) { | ||
| 50 | /* Get rid of src ip and src pt */ | ||
| 51 | oldip = iph->saddr; | ||
| 52 | newip = tuple->src.u3.ip; | ||
| 53 | newport = tuple->src.u.udp.port; | ||
| 54 | portptr = &hdr->source; | ||
| 55 | } else { | ||
| 56 | /* Get rid of dst ip and dst pt */ | ||
| 57 | oldip = iph->daddr; | ||
| 58 | newip = tuple->dst.u3.ip; | ||
| 59 | newport = tuple->dst.u.udp.port; | ||
| 60 | portptr = &hdr->dest; | ||
| 61 | } | ||
| 62 | |||
| 63 | inet_proto_csum_replace4(&hdr->check, skb, oldip, newip, 1); | ||
| 64 | inet_proto_csum_replace2(&hdr->check, skb, *portptr, newport, 0); | ||
| 65 | if (!hdr->check) | ||
| 66 | hdr->check = CSUM_MANGLED_0; | ||
| 67 | |||
| 68 | *portptr = newport; | ||
| 69 | return true; | ||
| 70 | } | ||
| 71 | |||
| 72 | static const struct nf_nat_protocol nf_nat_protocol_udplite = { | ||
| 73 | .protonum = IPPROTO_UDPLITE, | ||
| 74 | .me = THIS_MODULE, | ||
| 75 | .manip_pkt = udplite_manip_pkt, | ||
| 76 | .in_range = nf_nat_proto_in_range, | ||
| 77 | .unique_tuple = udplite_unique_tuple, | ||
| 78 | #if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE) | ||
| 79 | .range_to_nlattr = nf_nat_proto_range_to_nlattr, | ||
| 80 | .nlattr_to_range = nf_nat_proto_nlattr_to_range, | ||
| 81 | #endif | ||
| 82 | }; | ||
| 83 | |||
| 84 | static int __init nf_nat_proto_udplite_init(void) | ||
| 85 | { | ||
| 86 | return nf_nat_protocol_register(&nf_nat_protocol_udplite); | ||
| 87 | } | ||
| 88 | |||
| 89 | static void __exit nf_nat_proto_udplite_fini(void) | ||
| 90 | { | ||
| 91 | nf_nat_protocol_unregister(&nf_nat_protocol_udplite); | ||
| 92 | } | ||
| 93 | |||
| 94 | module_init(nf_nat_proto_udplite_init); | ||
| 95 | module_exit(nf_nat_proto_udplite_fini); | ||
| 96 | |||
| 97 | MODULE_LICENSE("GPL"); | ||
| 98 | MODULE_DESCRIPTION("UDP-Lite NAT protocol helper"); | ||
| 99 | MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>"); | ||
diff --git a/net/ipv4/netfilter/nf_nat_proto_unknown.c b/net/ipv4/netfilter/nf_nat_proto_unknown.c new file mode 100644 index 00000000000..a50f2bc1c73 --- /dev/null +++ b/net/ipv4/netfilter/nf_nat_proto_unknown.c | |||
| @@ -0,0 +1,53 @@ | |||
| 1 | /* The "unknown" protocol. This is what is used for protocols we | ||
| 2 | * don't understand. It's returned by ip_ct_find_proto(). | ||
| 3 | */ | ||
| 4 | |||
| 5 | /* (C) 1999-2001 Paul `Rusty' Russell | ||
| 6 | * (C) 2002-2006 Netfilter Core Team <coreteam@netfilter.org> | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or modify | ||
| 9 | * it under the terms of the GNU General Public License version 2 as | ||
| 10 | * published by the Free Software Foundation. | ||
| 11 | */ | ||
| 12 | |||
| 13 | #include <linux/types.h> | ||
| 14 | #include <linux/init.h> | ||
| 15 | |||
| 16 | #include <linux/netfilter.h> | ||
| 17 | #include <net/netfilter/nf_nat.h> | ||
| 18 | #include <net/netfilter/nf_nat_rule.h> | ||
| 19 | #include <net/netfilter/nf_nat_protocol.h> | ||
| 20 | |||
| 21 | static bool unknown_in_range(const struct nf_conntrack_tuple *tuple, | ||
| 22 | enum nf_nat_manip_type manip_type, | ||
| 23 | const union nf_conntrack_man_proto *min, | ||
| 24 | const union nf_conntrack_man_proto *max) | ||
| 25 | { | ||
| 26 | return true; | ||
| 27 | } | ||
| 28 | |||
| 29 | static void unknown_unique_tuple(struct nf_conntrack_tuple *tuple, | ||
| 30 | const struct nf_nat_range *range, | ||
| 31 | enum nf_nat_manip_type maniptype, | ||
| 32 | const struct nf_conn *ct) | ||
| 33 | { | ||
| 34 | /* Sorry: we can't help you; if it's not unique, we can't frob | ||
| 35 | anything. */ | ||
| 36 | return; | ||
| 37 | } | ||
| 38 | |||
| 39 | static bool | ||
| 40 | unknown_manip_pkt(struct sk_buff *skb, | ||
| 41 | unsigned int iphdroff, | ||
| 42 | const struct nf_conntrack_tuple *tuple, | ||
| 43 | enum nf_nat_manip_type maniptype) | ||
| 44 | { | ||
| 45 | return true; | ||
| 46 | } | ||
| 47 | |||
| 48 | const struct nf_nat_protocol nf_nat_unknown_protocol = { | ||
| 49 | /* .me isn't set: getting a ref to this cannot fail. */ | ||
| 50 | .manip_pkt = unknown_manip_pkt, | ||
| 51 | .in_range = unknown_in_range, | ||
| 52 | .unique_tuple = unknown_unique_tuple, | ||
| 53 | }; | ||
diff --git a/net/ipv4/netfilter/nf_nat_rule.c b/net/ipv4/netfilter/nf_nat_rule.c new file mode 100644 index 00000000000..733c9abc1cb --- /dev/null +++ b/net/ipv4/netfilter/nf_nat_rule.c | |||
| @@ -0,0 +1,214 @@ | |||
| 1 | /* (C) 1999-2001 Paul `Rusty' Russell | ||
| 2 | * (C) 2002-2006 Netfilter Core Team <coreteam@netfilter.org> | ||
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify | ||
| 5 | * it under the terms of the GNU General Public License version 2 as | ||
| 6 | * published by the Free Software Foundation. | ||
| 7 | */ | ||
| 8 | |||
| 9 | /* Everything about the rules for NAT. */ | ||
| 10 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
| 11 | #include <linux/types.h> | ||
| 12 | #include <linux/ip.h> | ||
| 13 | #include <linux/netfilter.h> | ||
| 14 | #include <linux/netfilter_ipv4.h> | ||
| 15 | #include <linux/module.h> | ||
| 16 | #include <linux/kmod.h> | ||
| 17 | #include <linux/skbuff.h> | ||
| 18 | #include <linux/proc_fs.h> | ||
| 19 | #include <linux/slab.h> | ||
| 20 | #include <net/checksum.h> | ||
| 21 | #include <net/route.h> | ||
| 22 | #include <linux/bitops.h> | ||
| 23 | |||
| 24 | #include <linux/netfilter_ipv4/ip_tables.h> | ||
| 25 | #include <net/netfilter/nf_nat.h> | ||
| 26 | #include <net/netfilter/nf_nat_core.h> | ||
| 27 | #include <net/netfilter/nf_nat_rule.h> | ||
| 28 | |||
| 29 | #define NAT_VALID_HOOKS ((1 << NF_INET_PRE_ROUTING) | \ | ||
| 30 | (1 << NF_INET_POST_ROUTING) | \ | ||
| 31 | (1 << NF_INET_LOCAL_OUT) | \ | ||
| 32 | (1 << NF_INET_LOCAL_IN)) | ||
| 33 | |||
| 34 | static const struct xt_table nat_table = { | ||
| 35 | .name = "nat", | ||
| 36 | .valid_hooks = NAT_VALID_HOOKS, | ||
| 37 | .me = THIS_MODULE, | ||
| 38 | .af = NFPROTO_IPV4, | ||
| 39 | }; | ||
| 40 | |||
| 41 | /* Source NAT */ | ||
| 42 | static unsigned int | ||
| 43 | ipt_snat_target(struct sk_buff *skb, const struct xt_action_param *par) | ||
| 44 | { | ||
| 45 | struct nf_conn *ct; | ||
| 46 | enum ip_conntrack_info ctinfo; | ||
| 47 | const struct nf_nat_multi_range_compat *mr = par->targinfo; | ||
| 48 | |||
| 49 | NF_CT_ASSERT(par->hooknum == NF_INET_POST_ROUTING || | ||
| 50 | par->hooknum == NF_INET_LOCAL_IN); | ||
| 51 | |||
| 52 | ct = nf_ct_get(skb, &ctinfo); | ||
| 53 | |||
| 54 | /* Connection must be valid and new. */ | ||
| 55 | NF_CT_ASSERT(ct && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED || | ||
| 56 | ctinfo == IP_CT_RELATED_REPLY)); | ||
| 57 | NF_CT_ASSERT(par->out != NULL); | ||
| 58 | |||
| 59 | return nf_nat_setup_info(ct, &mr->range[0], IP_NAT_MANIP_SRC); | ||
| 60 | } | ||
| 61 | |||
| 62 | static unsigned int | ||
| 63 | ipt_dnat_target(struct sk_buff *skb, const struct xt_action_param *par) | ||
| 64 | { | ||
| 65 | struct nf_conn *ct; | ||
| 66 | enum ip_conntrack_info ctinfo; | ||
| 67 | const struct nf_nat_multi_range_compat *mr = par->targinfo; | ||
| 68 | |||
| 69 | NF_CT_ASSERT(par->hooknum == NF_INET_PRE_ROUTING || | ||
| 70 | par->hooknum == NF_INET_LOCAL_OUT); | ||
| 71 | |||
| 72 | ct = nf_ct_get(skb, &ctinfo); | ||
| 73 | |||
| 74 | /* Connection must be valid and new. */ | ||
| 75 | NF_CT_ASSERT(ct && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED)); | ||
| 76 | |||
| 77 | return nf_nat_setup_info(ct, &mr->range[0], IP_NAT_MANIP_DST); | ||
| 78 | } | ||
| 79 | |||
| 80 | static int ipt_snat_checkentry(const struct xt_tgchk_param *par) | ||
| 81 | { | ||
| 82 | const struct nf_nat_multi_range_compat *mr = par->targinfo; | ||
| 83 | |||
| 84 | /* Must be a valid range */ | ||
| 85 | if (mr->rangesize != 1) { | ||
| 86 | pr_info("SNAT: multiple ranges no longer supported\n"); | ||
| 87 | return -EINVAL; | ||
| 88 | } | ||
| 89 | return 0; | ||
| 90 | } | ||
| 91 | |||
| 92 | static int ipt_dnat_checkentry(const struct xt_tgchk_param *par) | ||
| 93 | { | ||
| 94 | const struct nf_nat_multi_range_compat *mr = par->targinfo; | ||
| 95 | |||
| 96 | /* Must be a valid range */ | ||
| 97 | if (mr->rangesize != 1) { | ||
| 98 | pr_info("DNAT: multiple ranges no longer supported\n"); | ||
| 99 | return -EINVAL; | ||
| 100 | } | ||
| 101 | return 0; | ||
| 102 | } | ||
| 103 | |||
| 104 | static unsigned int | ||
| 105 | alloc_null_binding(struct nf_conn *ct, unsigned int hooknum) | ||
| 106 | { | ||
| 107 | /* Force range to this IP; let proto decide mapping for | ||
| 108 | per-proto parts (hence not IP_NAT_RANGE_PROTO_SPECIFIED). | ||
| 109 | */ | ||
| 110 | struct nf_nat_range range; | ||
| 111 | |||
| 112 | range.flags = 0; | ||
| 113 | pr_debug("Allocating NULL binding for %p (%pI4)\n", ct, | ||
| 114 | HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC ? | ||
| 115 | &ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u3.ip : | ||
| 116 | &ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.u3.ip); | ||
| 117 | |||
| 118 | return nf_nat_setup_info(ct, &range, HOOK2MANIP(hooknum)); | ||
| 119 | } | ||
| 120 | |||
| 121 | int nf_nat_rule_find(struct sk_buff *skb, | ||
| 122 | unsigned int hooknum, | ||
| 123 | const struct net_device *in, | ||
| 124 | const struct net_device *out, | ||
| 125 | struct nf_conn *ct) | ||
| 126 | { | ||
| 127 | struct net *net = nf_ct_net(ct); | ||
| 128 | int ret; | ||
| 129 | |||
| 130 | ret = ipt_do_table(skb, hooknum, in, out, net->ipv4.nat_table); | ||
| 131 | |||
| 132 | if (ret == NF_ACCEPT) { | ||
| 133 | if (!nf_nat_initialized(ct, HOOK2MANIP(hooknum))) | ||
| 134 | /* NUL mapping */ | ||
| 135 | ret = alloc_null_binding(ct, hooknum); | ||
| 136 | } | ||
| 137 | return ret; | ||
| 138 | } | ||
| 139 | |||
| 140 | static struct xt_target ipt_snat_reg __read_mostly = { | ||
| 141 | .name = "SNAT", | ||
| 142 | .target = ipt_snat_target, | ||
| 143 | .targetsize = sizeof(struct nf_nat_multi_range_compat), | ||
| 144 | .table = "nat", | ||
| 145 | .hooks = (1 << NF_INET_POST_ROUTING) | (1 << NF_INET_LOCAL_IN), | ||
| 146 | .checkentry = ipt_snat_checkentry, | ||
| 147 | .family = AF_INET, | ||
| 148 | }; | ||
| 149 | |||
| 150 | static struct xt_target ipt_dnat_reg __read_mostly = { | ||
| 151 | .name = "DNAT", | ||
| 152 | .target = ipt_dnat_target, | ||
| 153 | .targetsize = sizeof(struct nf_nat_multi_range_compat), | ||
| 154 | .table = "nat", | ||
| 155 | .hooks = (1 << NF_INET_PRE_ROUTING) | (1 << NF_INET_LOCAL_OUT), | ||
| 156 | .checkentry = ipt_dnat_checkentry, | ||
| 157 | .family = AF_INET, | ||
| 158 | }; | ||
| 159 | |||
| 160 | static int __net_init nf_nat_rule_net_init(struct net *net) | ||
| 161 | { | ||
| 162 | struct ipt_replace *repl; | ||
| 163 | |||
| 164 | repl = ipt_alloc_initial_table(&nat_table); | ||
| 165 | if (repl == NULL) | ||
| 166 | return -ENOMEM; | ||
| 167 | net->ipv4.nat_table = ipt_register_table(net, &nat_table, repl); | ||
| 168 | kfree(repl); | ||
| 169 | if (IS_ERR(net->ipv4.nat_table)) | ||
| 170 | return PTR_ERR(net->ipv4.nat_table); | ||
| 171 | return 0; | ||
| 172 | } | ||
| 173 | |||
| 174 | static void __net_exit nf_nat_rule_net_exit(struct net *net) | ||
| 175 | { | ||
| 176 | ipt_unregister_table(net, net->ipv4.nat_table); | ||
| 177 | } | ||
| 178 | |||
| 179 | static struct pernet_operations nf_nat_rule_net_ops = { | ||
| 180 | .init = nf_nat_rule_net_init, | ||
| 181 | .exit = nf_nat_rule_net_exit, | ||
| 182 | }; | ||
| 183 | |||
| 184 | int __init nf_nat_rule_init(void) | ||
| 185 | { | ||
| 186 | int ret; | ||
| 187 | |||
| 188 | ret = register_pernet_subsys(&nf_nat_rule_net_ops); | ||
| 189 | if (ret != 0) | ||
| 190 | goto out; | ||
| 191 | ret = xt_register_target(&ipt_snat_reg); | ||
| 192 | if (ret != 0) | ||
| 193 | goto unregister_table; | ||
| 194 | |||
| 195 | ret = xt_register_target(&ipt_dnat_reg); | ||
| 196 | if (ret != 0) | ||
| 197 | goto unregister_snat; | ||
| 198 | |||
| 199 | return ret; | ||
| 200 | |||
| 201 | unregister_snat: | ||
| 202 | xt_unregister_target(&ipt_snat_reg); | ||
| 203 | unregister_table: | ||
| 204 | unregister_pernet_subsys(&nf_nat_rule_net_ops); | ||
| 205 | out: | ||
| 206 | return ret; | ||
| 207 | } | ||
| 208 | |||
| 209 | void nf_nat_rule_cleanup(void) | ||
| 210 | { | ||
| 211 | xt_unregister_target(&ipt_dnat_reg); | ||
| 212 | xt_unregister_target(&ipt_snat_reg); | ||
| 213 | unregister_pernet_subsys(&nf_nat_rule_net_ops); | ||
| 214 | } | ||
diff --git a/net/ipv4/netfilter/nf_nat_sip.c b/net/ipv4/netfilter/nf_nat_sip.c new file mode 100644 index 00000000000..e40cf7816fd --- /dev/null +++ b/net/ipv4/netfilter/nf_nat_sip.c | |||
| @@ -0,0 +1,561 @@ | |||
| 1 | /* SIP extension for NAT alteration. | ||
| 2 | * | ||
| 3 | * (C) 2005 by Christian Hentschel <chentschel@arnet.com.ar> | ||
| 4 | * based on RR's ip_nat_ftp.c and other modules. | ||
| 5 | * (C) 2007 United Security Providers | ||
| 6 | * (C) 2007, 2008 Patrick McHardy <kaber@trash.net> | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or modify | ||
| 9 | * it under the terms of the GNU General Public License version 2 as | ||
| 10 | * published by the Free Software Foundation. | ||
| 11 | */ | ||
| 12 | |||
| 13 | #include <linux/module.h> | ||
| 14 | #include <linux/skbuff.h> | ||
| 15 | #include <linux/ip.h> | ||
| 16 | #include <net/ip.h> | ||
| 17 | #include <linux/udp.h> | ||
| 18 | #include <linux/tcp.h> | ||
| 19 | |||
| 20 | #include <net/netfilter/nf_nat.h> | ||
| 21 | #include <net/netfilter/nf_nat_helper.h> | ||
| 22 | #include <net/netfilter/nf_nat_rule.h> | ||
| 23 | #include <net/netfilter/nf_conntrack_helper.h> | ||
| 24 | #include <net/netfilter/nf_conntrack_expect.h> | ||
| 25 | #include <linux/netfilter/nf_conntrack_sip.h> | ||
| 26 | |||
| 27 | MODULE_LICENSE("GPL"); | ||
| 28 | MODULE_AUTHOR("Christian Hentschel <chentschel@arnet.com.ar>"); | ||
| 29 | MODULE_DESCRIPTION("SIP NAT helper"); | ||
| 30 | MODULE_ALIAS("ip_nat_sip"); | ||
| 31 | |||
| 32 | |||
| 33 | static unsigned int mangle_packet(struct sk_buff *skb, unsigned int dataoff, | ||
| 34 | const char **dptr, unsigned int *datalen, | ||
| 35 | unsigned int matchoff, unsigned int matchlen, | ||
| 36 | const char *buffer, unsigned int buflen) | ||
| 37 | { | ||
| 38 | enum ip_conntrack_info ctinfo; | ||
| 39 | struct nf_conn *ct = nf_ct_get(skb, &ctinfo); | ||
| 40 | struct tcphdr *th; | ||
| 41 | unsigned int baseoff; | ||
| 42 | |||
| 43 | if (nf_ct_protonum(ct) == IPPROTO_TCP) { | ||
| 44 | th = (struct tcphdr *)(skb->data + ip_hdrlen(skb)); | ||
| 45 | baseoff = ip_hdrlen(skb) + th->doff * 4; | ||
| 46 | matchoff += dataoff - baseoff; | ||
| 47 | |||
| 48 | if (!__nf_nat_mangle_tcp_packet(skb, ct, ctinfo, | ||
| 49 | matchoff, matchlen, | ||
| 50 | buffer, buflen, false)) | ||
| 51 | return 0; | ||
| 52 | } else { | ||
| 53 | baseoff = ip_hdrlen(skb) + sizeof(struct udphdr); | ||
| 54 | matchoff += dataoff - baseoff; | ||
| 55 | |||
| 56 | if (!nf_nat_mangle_udp_packet(skb, ct, ctinfo, | ||
| 57 | matchoff, matchlen, | ||
| 58 | buffer, buflen)) | ||
| 59 | return 0; | ||
| 60 | } | ||
| 61 | |||
| 62 | /* Reload data pointer and adjust datalen value */ | ||
| 63 | *dptr = skb->data + dataoff; | ||
| 64 | *datalen += buflen - matchlen; | ||
| 65 | return 1; | ||
| 66 | } | ||
| 67 | |||
| 68 | static int map_addr(struct sk_buff *skb, unsigned int dataoff, | ||
| 69 | const char **dptr, unsigned int *datalen, | ||
| 70 | unsigned int matchoff, unsigned int matchlen, | ||
| 71 | union nf_inet_addr *addr, __be16 port) | ||
| 72 | { | ||
| 73 | enum ip_conntrack_info ctinfo; | ||
| 74 | struct nf_conn *ct = nf_ct_get(skb, &ctinfo); | ||
| 75 | enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); | ||
| 76 | char buffer[sizeof("nnn.nnn.nnn.nnn:nnnnn")]; | ||
| 77 | unsigned int buflen; | ||
| 78 | __be32 newaddr; | ||
| 79 | __be16 newport; | ||
| 80 | |||
| 81 | if (ct->tuplehash[dir].tuple.src.u3.ip == addr->ip && | ||
| 82 | ct->tuplehash[dir].tuple.src.u.udp.port == port) { | ||
| 83 | newaddr = ct->tuplehash[!dir].tuple.dst.u3.ip; | ||
| 84 | newport = ct->tuplehash[!dir].tuple.dst.u.udp.port; | ||
| 85 | } else if (ct->tuplehash[dir].tuple.dst.u3.ip == addr->ip && | ||
| 86 | ct->tuplehash[dir].tuple.dst.u.udp.port == port) { | ||
| 87 | newaddr = ct->tuplehash[!dir].tuple.src.u3.ip; | ||
| 88 | newport = ct->tuplehash[!dir].tuple.src.u.udp.port; | ||
| 89 | } else | ||
| 90 | return 1; | ||
| 91 | |||
| 92 | if (newaddr == addr->ip && newport == port) | ||
| 93 | return 1; | ||
| 94 | |||
| 95 | buflen = sprintf(buffer, "%pI4:%u", &newaddr, ntohs(newport)); | ||
| 96 | |||
| 97 | return mangle_packet(skb, dataoff, dptr, datalen, matchoff, matchlen, | ||
| 98 | buffer, buflen); | ||
| 99 | } | ||
| 100 | |||
| 101 | static int map_sip_addr(struct sk_buff *skb, unsigned int dataoff, | ||
| 102 | const char **dptr, unsigned int *datalen, | ||
| 103 | enum sip_header_types type) | ||
| 104 | { | ||
| 105 | enum ip_conntrack_info ctinfo; | ||
| 106 | struct nf_conn *ct = nf_ct_get(skb, &ctinfo); | ||
| 107 | unsigned int matchlen, matchoff; | ||
| 108 | union nf_inet_addr addr; | ||
| 109 | __be16 port; | ||
| 110 | |||
| 111 | if (ct_sip_parse_header_uri(ct, *dptr, NULL, *datalen, type, NULL, | ||
| 112 | &matchoff, &matchlen, &addr, &port) <= 0) | ||
| 113 | return 1; | ||
| 114 | return map_addr(skb, dataoff, dptr, datalen, matchoff, matchlen, | ||
| 115 | &addr, port); | ||
| 116 | } | ||
| 117 | |||
| 118 | static unsigned int ip_nat_sip(struct sk_buff *skb, unsigned int dataoff, | ||
| 119 | const char **dptr, unsigned int *datalen) | ||
| 120 | { | ||
| 121 | enum ip_conntrack_info ctinfo; | ||
| 122 | struct nf_conn *ct = nf_ct_get(skb, &ctinfo); | ||
| 123 | enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); | ||
| 124 | unsigned int coff, matchoff, matchlen; | ||
| 125 | enum sip_header_types hdr; | ||
| 126 | union nf_inet_addr addr; | ||
| 127 | __be16 port; | ||
| 128 | int request, in_header; | ||
| 129 | |||
| 130 | /* Basic rules: requests and responses. */ | ||
| 131 | if (strnicmp(*dptr, "SIP/2.0", strlen("SIP/2.0")) != 0) { | ||
| 132 | if (ct_sip_parse_request(ct, *dptr, *datalen, | ||
| 133 | &matchoff, &matchlen, | ||
| 134 | &addr, &port) > 0 && | ||
| 135 | !map_addr(skb, dataoff, dptr, datalen, matchoff, matchlen, | ||
| 136 | &addr, port)) | ||
| 137 | return NF_DROP; | ||
| 138 | request = 1; | ||
| 139 | } else | ||
| 140 | request = 0; | ||
| 141 | |||
| 142 | if (nf_ct_protonum(ct) == IPPROTO_TCP) | ||
| 143 | hdr = SIP_HDR_VIA_TCP; | ||
| 144 | else | ||
| 145 | hdr = SIP_HDR_VIA_UDP; | ||
| 146 | |||
| 147 | /* Translate topmost Via header and parameters */ | ||
| 148 | if (ct_sip_parse_header_uri(ct, *dptr, NULL, *datalen, | ||
| 149 | hdr, NULL, &matchoff, &matchlen, | ||
| 150 | &addr, &port) > 0) { | ||
| 151 | unsigned int matchend, poff, plen, buflen, n; | ||
| 152 | char buffer[sizeof("nnn.nnn.nnn.nnn:nnnnn")]; | ||
| 153 | |||
| 154 | /* We're only interested in headers related to this | ||
| 155 | * connection */ | ||
| 156 | if (request) { | ||
| 157 | if (addr.ip != ct->tuplehash[dir].tuple.src.u3.ip || | ||
| 158 | port != ct->tuplehash[dir].tuple.src.u.udp.port) | ||
| 159 | goto next; | ||
| 160 | } else { | ||
| 161 | if (addr.ip != ct->tuplehash[dir].tuple.dst.u3.ip || | ||
| 162 | port != ct->tuplehash[dir].tuple.dst.u.udp.port) | ||
| 163 | goto next; | ||
| 164 | } | ||
| 165 | |||
| 166 | if (!map_addr(skb, dataoff, dptr, datalen, matchoff, matchlen, | ||
| 167 | &addr, port)) | ||
| 168 | return NF_DROP; | ||
| 169 | |||
| 170 | matchend = matchoff + matchlen; | ||
| 171 | |||
| 172 | /* The maddr= parameter (RFC 2361) specifies where to send | ||
| 173 | * the reply. */ | ||
| 174 | if (ct_sip_parse_address_param(ct, *dptr, matchend, *datalen, | ||
| 175 | "maddr=", &poff, &plen, | ||
| 176 | &addr) > 0 && | ||
| 177 | addr.ip == ct->tuplehash[dir].tuple.src.u3.ip && | ||
| 178 | addr.ip != ct->tuplehash[!dir].tuple.dst.u3.ip) { | ||
| 179 | buflen = sprintf(buffer, "%pI4", | ||
| 180 | &ct->tuplehash[!dir].tuple.dst.u3.ip); | ||
| 181 | if (!mangle_packet(skb, dataoff, dptr, datalen, | ||
| 182 | poff, plen, buffer, buflen)) | ||
| 183 | return NF_DROP; | ||
| 184 | } | ||
| 185 | |||
| 186 | /* The received= parameter (RFC 2361) contains the address | ||
| 187 | * from which the server received the request. */ | ||
| 188 | if (ct_sip_parse_address_param(ct, *dptr, matchend, *datalen, | ||
| 189 | "received=", &poff, &plen, | ||
| 190 | &addr) > 0 && | ||
| 191 | addr.ip == ct->tuplehash[dir].tuple.dst.u3.ip && | ||
| 192 | addr.ip != ct->tuplehash[!dir].tuple.src.u3.ip) { | ||
| 193 | buflen = sprintf(buffer, "%pI4", | ||
| 194 | &ct->tuplehash[!dir].tuple.src.u3.ip); | ||
| 195 | if (!mangle_packet(skb, dataoff, dptr, datalen, | ||
| 196 | poff, plen, buffer, buflen)) | ||
| 197 | return NF_DROP; | ||
| 198 | } | ||
| 199 | |||
| 200 | /* The rport= parameter (RFC 3581) contains the port number | ||
| 201 | * from which the server received the request. */ | ||
| 202 | if (ct_sip_parse_numerical_param(ct, *dptr, matchend, *datalen, | ||
| 203 | "rport=", &poff, &plen, | ||
| 204 | &n) > 0 && | ||
| 205 | htons(n) == ct->tuplehash[dir].tuple.dst.u.udp.port && | ||
| 206 | htons(n) != ct->tuplehash[!dir].tuple.src.u.udp.port) { | ||
| 207 | __be16 p = ct->tuplehash[!dir].tuple.src.u.udp.port; | ||
| 208 | buflen = sprintf(buffer, "%u", ntohs(p)); | ||
| 209 | if (!mangle_packet(skb, dataoff, dptr, datalen, | ||
| 210 | poff, plen, buffer, buflen)) | ||
| 211 | return NF_DROP; | ||
| 212 | } | ||
| 213 | } | ||
| 214 | |||
| 215 | next: | ||
| 216 | /* Translate Contact headers */ | ||
| 217 | coff = 0; | ||
| 218 | in_header = 0; | ||
| 219 | while (ct_sip_parse_header_uri(ct, *dptr, &coff, *datalen, | ||
| 220 | SIP_HDR_CONTACT, &in_header, | ||
| 221 | &matchoff, &matchlen, | ||
| 222 | &addr, &port) > 0) { | ||
| 223 | if (!map_addr(skb, dataoff, dptr, datalen, matchoff, matchlen, | ||
| 224 | &addr, port)) | ||
| 225 | return NF_DROP; | ||
| 226 | } | ||
| 227 | |||
| 228 | if (!map_sip_addr(skb, dataoff, dptr, datalen, SIP_HDR_FROM) || | ||
| 229 | !map_sip_addr(skb, dataoff, dptr, datalen, SIP_HDR_TO)) | ||
| 230 | return NF_DROP; | ||
| 231 | |||
| 232 | return NF_ACCEPT; | ||
| 233 | } | ||
| 234 | |||
| 235 | static void ip_nat_sip_seq_adjust(struct sk_buff *skb, s16 off) | ||
| 236 | { | ||
| 237 | enum ip_conntrack_info ctinfo; | ||
| 238 | struct nf_conn *ct = nf_ct_get(skb, &ctinfo); | ||
| 239 | const struct tcphdr *th; | ||
| 240 | |||
| 241 | if (nf_ct_protonum(ct) != IPPROTO_TCP || off == 0) | ||
| 242 | return; | ||
| 243 | |||
| 244 | th = (struct tcphdr *)(skb->data + ip_hdrlen(skb)); | ||
| 245 | nf_nat_set_seq_adjust(ct, ctinfo, th->seq, off); | ||
| 246 | } | ||
| 247 | |||
| 248 | /* Handles expected signalling connections and media streams */ | ||
| 249 | static void ip_nat_sip_expected(struct nf_conn *ct, | ||
| 250 | struct nf_conntrack_expect *exp) | ||
| 251 | { | ||
| 252 | struct nf_nat_range range; | ||
| 253 | |||
| 254 | /* This must be a fresh one. */ | ||
| 255 | BUG_ON(ct->status & IPS_NAT_DONE_MASK); | ||
| 256 | |||
| 257 | /* For DST manip, map port here to where it's expected. */ | ||
| 258 | range.flags = (IP_NAT_RANGE_MAP_IPS | IP_NAT_RANGE_PROTO_SPECIFIED); | ||
| 259 | range.min = range.max = exp->saved_proto; | ||
| 260 | range.min_ip = range.max_ip = exp->saved_ip; | ||
| 261 | nf_nat_setup_info(ct, &range, IP_NAT_MANIP_DST); | ||
| 262 | |||
| 263 | /* Change src to where master sends to, but only if the connection | ||
| 264 | * actually came from the same source. */ | ||
| 265 | if (ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3.ip == | ||
| 266 | ct->master->tuplehash[exp->dir].tuple.src.u3.ip) { | ||
| 267 | range.flags = IP_NAT_RANGE_MAP_IPS; | ||
| 268 | range.min_ip = range.max_ip | ||
| 269 | = ct->master->tuplehash[!exp->dir].tuple.dst.u3.ip; | ||
| 270 | nf_nat_setup_info(ct, &range, IP_NAT_MANIP_SRC); | ||
| 271 | } | ||
| 272 | } | ||
| 273 | |||
| 274 | static unsigned int ip_nat_sip_expect(struct sk_buff *skb, unsigned int dataoff, | ||
| 275 | const char **dptr, unsigned int *datalen, | ||
| 276 | struct nf_conntrack_expect *exp, | ||
| 277 | unsigned int matchoff, | ||
| 278 | unsigned int matchlen) | ||
| 279 | { | ||
| 280 | enum ip_conntrack_info ctinfo; | ||
| 281 | struct nf_conn *ct = nf_ct_get(skb, &ctinfo); | ||
| 282 | enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); | ||
| 283 | __be32 newip; | ||
| 284 | u_int16_t port; | ||
| 285 | char buffer[sizeof("nnn.nnn.nnn.nnn:nnnnn")]; | ||
| 286 | unsigned buflen; | ||
| 287 | |||
| 288 | /* Connection will come from reply */ | ||
| 289 | if (ct->tuplehash[dir].tuple.src.u3.ip == ct->tuplehash[!dir].tuple.dst.u3.ip) | ||
| 290 | newip = exp->tuple.dst.u3.ip; | ||
| 291 | else | ||
| 292 | newip = ct->tuplehash[!dir].tuple.dst.u3.ip; | ||
| 293 | |||
| 294 | /* If the signalling port matches the connection's source port in the | ||
| 295 | * original direction, try to use the destination port in the opposite | ||
| 296 | * direction. */ | ||
| 297 | if (exp->tuple.dst.u.udp.port == | ||
| 298 | ct->tuplehash[dir].tuple.src.u.udp.port) | ||
| 299 | port = ntohs(ct->tuplehash[!dir].tuple.dst.u.udp.port); | ||
| 300 | else | ||
| 301 | port = ntohs(exp->tuple.dst.u.udp.port); | ||
| 302 | |||
| 303 | exp->saved_ip = exp->tuple.dst.u3.ip; | ||
| 304 | exp->tuple.dst.u3.ip = newip; | ||
| 305 | exp->saved_proto.udp.port = exp->tuple.dst.u.udp.port; | ||
| 306 | exp->dir = !dir; | ||
| 307 | exp->expectfn = ip_nat_sip_expected; | ||
| 308 | |||
| 309 | for (; port != 0; port++) { | ||
| 310 | int ret; | ||
| 311 | |||
| 312 | exp->tuple.dst.u.udp.port = htons(port); | ||
| 313 | ret = nf_ct_expect_related(exp); | ||
| 314 | if (ret == 0) | ||
| 315 | break; | ||
| 316 | else if (ret != -EBUSY) { | ||
| 317 | port = 0; | ||
| 318 | break; | ||
| 319 | } | ||
| 320 | } | ||
| 321 | |||
| 322 | if (port == 0) | ||
| 323 | return NF_DROP; | ||
| 324 | |||
| 325 | if (exp->tuple.dst.u3.ip != exp->saved_ip || | ||
| 326 | exp->tuple.dst.u.udp.port != exp->saved_proto.udp.port) { | ||
| 327 | buflen = sprintf(buffer, "%pI4:%u", &newip, port); | ||
| 328 | if (!mangle_packet(skb, dataoff, dptr, datalen, | ||
| 329 | matchoff, matchlen, buffer, buflen)) | ||
| 330 | goto err; | ||
| 331 | } | ||
| 332 | return NF_ACCEPT; | ||
| 333 | |||
| 334 | err: | ||
| 335 | nf_ct_unexpect_related(exp); | ||
| 336 | return NF_DROP; | ||
| 337 | } | ||
| 338 | |||
| 339 | static int mangle_content_len(struct sk_buff *skb, unsigned int dataoff, | ||
| 340 | const char **dptr, unsigned int *datalen) | ||
| 341 | { | ||
| 342 | enum ip_conntrack_info ctinfo; | ||
| 343 | struct nf_conn *ct = nf_ct_get(skb, &ctinfo); | ||
| 344 | unsigned int matchoff, matchlen; | ||
| 345 | char buffer[sizeof("65536")]; | ||
| 346 | int buflen, c_len; | ||
| 347 | |||
| 348 | /* Get actual SDP length */ | ||
| 349 | if (ct_sip_get_sdp_header(ct, *dptr, 0, *datalen, | ||
| 350 | SDP_HDR_VERSION, SDP_HDR_UNSPEC, | ||
| 351 | &matchoff, &matchlen) <= 0) | ||
| 352 | return 0; | ||
| 353 | c_len = *datalen - matchoff + strlen("v="); | ||
| 354 | |||
| 355 | /* Now, update SDP length */ | ||
| 356 | if (ct_sip_get_header(ct, *dptr, 0, *datalen, SIP_HDR_CONTENT_LENGTH, | ||
| 357 | &matchoff, &matchlen) <= 0) | ||
| 358 | return 0; | ||
| 359 | |||
| 360 | buflen = sprintf(buffer, "%u", c_len); | ||
| 361 | return mangle_packet(skb, dataoff, dptr, datalen, matchoff, matchlen, | ||
| 362 | buffer, buflen); | ||
| 363 | } | ||
| 364 | |||
| 365 | static int mangle_sdp_packet(struct sk_buff *skb, unsigned int dataoff, | ||
| 366 | const char **dptr, unsigned int *datalen, | ||
| 367 | unsigned int sdpoff, | ||
| 368 | enum sdp_header_types type, | ||
| 369 | enum sdp_header_types term, | ||
| 370 | char *buffer, int buflen) | ||
| 371 | { | ||
| 372 | enum ip_conntrack_info ctinfo; | ||
| 373 | struct nf_conn *ct = nf_ct_get(skb, &ctinfo); | ||
| 374 | unsigned int matchlen, matchoff; | ||
| 375 | |||
| 376 | if (ct_sip_get_sdp_header(ct, *dptr, sdpoff, *datalen, type, term, | ||
| 377 | &matchoff, &matchlen) <= 0) | ||
| 378 | return -ENOENT; | ||
| 379 | return mangle_packet(skb, dataoff, dptr, datalen, matchoff, matchlen, | ||
| 380 | buffer, buflen) ? 0 : -EINVAL; | ||
| 381 | } | ||
| 382 | |||
| 383 | static unsigned int ip_nat_sdp_addr(struct sk_buff *skb, unsigned int dataoff, | ||
| 384 | const char **dptr, unsigned int *datalen, | ||
| 385 | unsigned int sdpoff, | ||
| 386 | enum sdp_header_types type, | ||
| 387 | enum sdp_header_types term, | ||
| 388 | const union nf_inet_addr *addr) | ||
| 389 | { | ||
| 390 | char buffer[sizeof("nnn.nnn.nnn.nnn")]; | ||
| 391 | unsigned int buflen; | ||
| 392 | |||
| 393 | buflen = sprintf(buffer, "%pI4", &addr->ip); | ||
| 394 | if (mangle_sdp_packet(skb, dataoff, dptr, datalen, sdpoff, type, term, | ||
| 395 | buffer, buflen)) | ||
| 396 | return 0; | ||
| 397 | |||
| 398 | return mangle_content_len(skb, dataoff, dptr, datalen); | ||
| 399 | } | ||
| 400 | |||
| 401 | static unsigned int ip_nat_sdp_port(struct sk_buff *skb, unsigned int dataoff, | ||
| 402 | const char **dptr, unsigned int *datalen, | ||
| 403 | unsigned int matchoff, | ||
| 404 | unsigned int matchlen, | ||
| 405 | u_int16_t port) | ||
| 406 | { | ||
| 407 | char buffer[sizeof("nnnnn")]; | ||
| 408 | unsigned int buflen; | ||
| 409 | |||
| 410 | buflen = sprintf(buffer, "%u", port); | ||
| 411 | if (!mangle_packet(skb, dataoff, dptr, datalen, matchoff, matchlen, | ||
| 412 | buffer, buflen)) | ||
| 413 | return 0; | ||
| 414 | |||
| 415 | return mangle_content_len(skb, dataoff, dptr, datalen); | ||
| 416 | } | ||
| 417 | |||
| 418 | static unsigned int ip_nat_sdp_session(struct sk_buff *skb, unsigned int dataoff, | ||
| 419 | const char **dptr, unsigned int *datalen, | ||
| 420 | unsigned int sdpoff, | ||
| 421 | const union nf_inet_addr *addr) | ||
| 422 | { | ||
| 423 | char buffer[sizeof("nnn.nnn.nnn.nnn")]; | ||
| 424 | unsigned int buflen; | ||
| 425 | |||
| 426 | /* Mangle session description owner and contact addresses */ | ||
| 427 | buflen = sprintf(buffer, "%pI4", &addr->ip); | ||
| 428 | if (mangle_sdp_packet(skb, dataoff, dptr, datalen, sdpoff, | ||
| 429 | SDP_HDR_OWNER_IP4, SDP_HDR_MEDIA, | ||
| 430 | buffer, buflen)) | ||
| 431 | return 0; | ||
| 432 | |||
| 433 | switch (mangle_sdp_packet(skb, dataoff, dptr, datalen, sdpoff, | ||
| 434 | SDP_HDR_CONNECTION_IP4, SDP_HDR_MEDIA, | ||
| 435 | buffer, buflen)) { | ||
| 436 | case 0: | ||
| 437 | /* | ||
| 438 | * RFC 2327: | ||
| 439 | * | ||
| 440 | * Session description | ||
| 441 | * | ||
| 442 | * c=* (connection information - not required if included in all media) | ||
| 443 | */ | ||
| 444 | case -ENOENT: | ||
| 445 | break; | ||
| 446 | default: | ||
| 447 | return 0; | ||
| 448 | } | ||
| 449 | |||
| 450 | return mangle_content_len(skb, dataoff, dptr, datalen); | ||
| 451 | } | ||
| 452 | |||
| 453 | /* So, this packet has hit the connection tracking matching code. | ||
| 454 | Mangle it, and change the expectation to match the new version. */ | ||
| 455 | static unsigned int ip_nat_sdp_media(struct sk_buff *skb, unsigned int dataoff, | ||
| 456 | const char **dptr, unsigned int *datalen, | ||
| 457 | struct nf_conntrack_expect *rtp_exp, | ||
| 458 | struct nf_conntrack_expect *rtcp_exp, | ||
| 459 | unsigned int mediaoff, | ||
| 460 | unsigned int medialen, | ||
| 461 | union nf_inet_addr *rtp_addr) | ||
| 462 | { | ||
| 463 | enum ip_conntrack_info ctinfo; | ||
| 464 | struct nf_conn *ct = nf_ct_get(skb, &ctinfo); | ||
| 465 | enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); | ||
| 466 | u_int16_t port; | ||
| 467 | |||
| 468 | /* Connection will come from reply */ | ||
| 469 | if (ct->tuplehash[dir].tuple.src.u3.ip == | ||
| 470 | ct->tuplehash[!dir].tuple.dst.u3.ip) | ||
| 471 | rtp_addr->ip = rtp_exp->tuple.dst.u3.ip; | ||
| 472 | else | ||
| 473 | rtp_addr->ip = ct->tuplehash[!dir].tuple.dst.u3.ip; | ||
| 474 | |||
| 475 | rtp_exp->saved_ip = rtp_exp->tuple.dst.u3.ip; | ||
| 476 | rtp_exp->tuple.dst.u3.ip = rtp_addr->ip; | ||
| 477 | rtp_exp->saved_proto.udp.port = rtp_exp->tuple.dst.u.udp.port; | ||
| 478 | rtp_exp->dir = !dir; | ||
| 479 | rtp_exp->expectfn = ip_nat_sip_expected; | ||
| 480 | |||
| 481 | rtcp_exp->saved_ip = rtcp_exp->tuple.dst.u3.ip; | ||
| 482 | rtcp_exp->tuple.dst.u3.ip = rtp_addr->ip; | ||
| 483 | rtcp_exp->saved_proto.udp.port = rtcp_exp->tuple.dst.u.udp.port; | ||
| 484 | rtcp_exp->dir = !dir; | ||
| 485 | rtcp_exp->expectfn = ip_nat_sip_expected; | ||
| 486 | |||
| 487 | /* Try to get same pair of ports: if not, try to change them. */ | ||
| 488 | for (port = ntohs(rtp_exp->tuple.dst.u.udp.port); | ||
| 489 | port != 0; port += 2) { | ||
| 490 | int ret; | ||
| 491 | |||
| 492 | rtp_exp->tuple.dst.u.udp.port = htons(port); | ||
| 493 | ret = nf_ct_expect_related(rtp_exp); | ||
| 494 | if (ret == -EBUSY) | ||
| 495 | continue; | ||
| 496 | else if (ret < 0) { | ||
| 497 | port = 0; | ||
| 498 | break; | ||
| 499 | } | ||
| 500 | rtcp_exp->tuple.dst.u.udp.port = htons(port + 1); | ||
| 501 | ret = nf_ct_expect_related(rtcp_exp); | ||
| 502 | if (ret == 0) | ||
| 503 | break; | ||
| 504 | else if (ret != -EBUSY) { | ||
| 505 | nf_ct_unexpect_related(rtp_exp); | ||
| 506 | port = 0; | ||
| 507 | break; | ||
| 508 | } | ||
| 509 | } | ||
| 510 | |||
| 511 | if (port == 0) | ||
| 512 | goto err1; | ||
| 513 | |||
| 514 | /* Update media port. */ | ||
| 515 | if (rtp_exp->tuple.dst.u.udp.port != rtp_exp->saved_proto.udp.port && | ||
| 516 | !ip_nat_sdp_port(skb, dataoff, dptr, datalen, | ||
| 517 | mediaoff, medialen, port)) | ||
| 518 | goto err2; | ||
| 519 | |||
| 520 | return NF_ACCEPT; | ||
| 521 | |||
| 522 | err2: | ||
| 523 | nf_ct_unexpect_related(rtp_exp); | ||
| 524 | nf_ct_unexpect_related(rtcp_exp); | ||
| 525 | err1: | ||
| 526 | return NF_DROP; | ||
| 527 | } | ||
| 528 | |||
| 529 | static void __exit nf_nat_sip_fini(void) | ||
| 530 | { | ||
| 531 | rcu_assign_pointer(nf_nat_sip_hook, NULL); | ||
| 532 | rcu_assign_pointer(nf_nat_sip_seq_adjust_hook, NULL); | ||
| 533 | rcu_assign_pointer(nf_nat_sip_expect_hook, NULL); | ||
| 534 | rcu_assign_pointer(nf_nat_sdp_addr_hook, NULL); | ||
| 535 | rcu_assign_pointer(nf_nat_sdp_port_hook, NULL); | ||
| 536 | rcu_assign_pointer(nf_nat_sdp_session_hook, NULL); | ||
| 537 | rcu_assign_pointer(nf_nat_sdp_media_hook, NULL); | ||
| 538 | synchronize_rcu(); | ||
| 539 | } | ||
| 540 | |||
| 541 | static int __init nf_nat_sip_init(void) | ||
| 542 | { | ||
| 543 | BUG_ON(nf_nat_sip_hook != NULL); | ||
| 544 | BUG_ON(nf_nat_sip_seq_adjust_hook != NULL); | ||
| 545 | BUG_ON(nf_nat_sip_expect_hook != NULL); | ||
| 546 | BUG_ON(nf_nat_sdp_addr_hook != NULL); | ||
| 547 | BUG_ON(nf_nat_sdp_port_hook != NULL); | ||
| 548 | BUG_ON(nf_nat_sdp_session_hook != NULL); | ||
| 549 | BUG_ON(nf_nat_sdp_media_hook != NULL); | ||
| 550 | rcu_assign_pointer(nf_nat_sip_hook, ip_nat_sip); | ||
| 551 | rcu_assign_pointer(nf_nat_sip_seq_adjust_hook, ip_nat_sip_seq_adjust); | ||
| 552 | rcu_assign_pointer(nf_nat_sip_expect_hook, ip_nat_sip_expect); | ||
| 553 | rcu_assign_pointer(nf_nat_sdp_addr_hook, ip_nat_sdp_addr); | ||
| 554 | rcu_assign_pointer(nf_nat_sdp_port_hook, ip_nat_sdp_port); | ||
| 555 | rcu_assign_pointer(nf_nat_sdp_session_hook, ip_nat_sdp_session); | ||
| 556 | rcu_assign_pointer(nf_nat_sdp_media_hook, ip_nat_sdp_media); | ||
| 557 | return 0; | ||
| 558 | } | ||
| 559 | |||
| 560 | module_init(nf_nat_sip_init); | ||
| 561 | module_exit(nf_nat_sip_fini); | ||
diff --git a/net/ipv4/netfilter/nf_nat_standalone.c b/net/ipv4/netfilter/nf_nat_standalone.c new file mode 100644 index 00000000000..a6e606e8482 --- /dev/null +++ b/net/ipv4/netfilter/nf_nat_standalone.c | |||
| @@ -0,0 +1,326 @@ | |||
| 1 | /* (C) 1999-2001 Paul `Rusty' Russell | ||
| 2 | * (C) 2002-2006 Netfilter Core Team <coreteam@netfilter.org> | ||
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify | ||
| 5 | * it under the terms of the GNU General Public License version 2 as | ||
| 6 | * published by the Free Software Foundation. | ||
| 7 | */ | ||
| 8 | #include <linux/types.h> | ||
| 9 | #include <linux/icmp.h> | ||
| 10 | #include <linux/gfp.h> | ||
| 11 | #include <linux/ip.h> | ||
| 12 | #include <linux/netfilter.h> | ||
| 13 | #include <linux/netfilter_ipv4.h> | ||
| 14 | #include <linux/module.h> | ||
| 15 | #include <linux/skbuff.h> | ||
| 16 | #include <linux/proc_fs.h> | ||
| 17 | #include <net/ip.h> | ||
| 18 | #include <net/checksum.h> | ||
| 19 | #include <linux/spinlock.h> | ||
| 20 | |||
| 21 | #include <net/netfilter/nf_conntrack.h> | ||
| 22 | #include <net/netfilter/nf_conntrack_core.h> | ||
| 23 | #include <net/netfilter/nf_conntrack_extend.h> | ||
| 24 | #include <net/netfilter/nf_nat.h> | ||
| 25 | #include <net/netfilter/nf_nat_rule.h> | ||
| 26 | #include <net/netfilter/nf_nat_protocol.h> | ||
| 27 | #include <net/netfilter/nf_nat_core.h> | ||
| 28 | #include <net/netfilter/nf_nat_helper.h> | ||
| 29 | #include <linux/netfilter_ipv4/ip_tables.h> | ||
| 30 | |||
| 31 | #ifdef CONFIG_XFRM | ||
| 32 | static void nat_decode_session(struct sk_buff *skb, struct flowi *fl) | ||
| 33 | { | ||
| 34 | struct flowi4 *fl4 = &fl->u.ip4; | ||
| 35 | const struct nf_conn *ct; | ||
| 36 | const struct nf_conntrack_tuple *t; | ||
| 37 | enum ip_conntrack_info ctinfo; | ||
| 38 | enum ip_conntrack_dir dir; | ||
| 39 | unsigned long statusbit; | ||
| 40 | |||
| 41 | ct = nf_ct_get(skb, &ctinfo); | ||
| 42 | if (ct == NULL) | ||
| 43 | return; | ||
| 44 | dir = CTINFO2DIR(ctinfo); | ||
| 45 | t = &ct->tuplehash[dir].tuple; | ||
| 46 | |||
| 47 | if (dir == IP_CT_DIR_ORIGINAL) | ||
| 48 | statusbit = IPS_DST_NAT; | ||
| 49 | else | ||
| 50 | statusbit = IPS_SRC_NAT; | ||
| 51 | |||
| 52 | if (ct->status & statusbit) { | ||
| 53 | fl4->daddr = t->dst.u3.ip; | ||
| 54 | if (t->dst.protonum == IPPROTO_TCP || | ||
| 55 | t->dst.protonum == IPPROTO_UDP || | ||
| 56 | t->dst.protonum == IPPROTO_UDPLITE || | ||
| 57 | t->dst.protonum == IPPROTO_DCCP || | ||
| 58 | t->dst.protonum == IPPROTO_SCTP) | ||
| 59 | fl4->fl4_dport = t->dst.u.tcp.port; | ||
| 60 | } | ||
| 61 | |||
| 62 | statusbit ^= IPS_NAT_MASK; | ||
| 63 | |||
| 64 | if (ct->status & statusbit) { | ||
| 65 | fl4->saddr = t->src.u3.ip; | ||
| 66 | if (t->dst.protonum == IPPROTO_TCP || | ||
| 67 | t->dst.protonum == IPPROTO_UDP || | ||
| 68 | t->dst.protonum == IPPROTO_UDPLITE || | ||
| 69 | t->dst.protonum == IPPROTO_DCCP || | ||
| 70 | t->dst.protonum == IPPROTO_SCTP) | ||
| 71 | fl4->fl4_sport = t->src.u.tcp.port; | ||
| 72 | } | ||
| 73 | } | ||
| 74 | #endif | ||
| 75 | |||
| 76 | static unsigned int | ||
| 77 | nf_nat_fn(unsigned int hooknum, | ||
| 78 | struct sk_buff *skb, | ||
| 79 | const struct net_device *in, | ||
| 80 | const struct net_device *out, | ||
| 81 | int (*okfn)(struct sk_buff *)) | ||
| 82 | { | ||
| 83 | struct nf_conn *ct; | ||
| 84 | enum ip_conntrack_info ctinfo; | ||
| 85 | struct nf_conn_nat *nat; | ||
| 86 | /* maniptype == SRC for postrouting. */ | ||
| 87 | enum nf_nat_manip_type maniptype = HOOK2MANIP(hooknum); | ||
| 88 | |||
| 89 | /* We never see fragments: conntrack defrags on pre-routing | ||
| 90 | and local-out, and nf_nat_out protects post-routing. */ | ||
| 91 | NF_CT_ASSERT(!ip_is_fragment(ip_hdr(skb))); | ||
| 92 | |||
| 93 | ct = nf_ct_get(skb, &ctinfo); | ||
| 94 | /* Can't track? It's not due to stress, or conntrack would | ||
| 95 | have dropped it. Hence it's the user's responsibilty to | ||
| 96 | packet filter it out, or implement conntrack/NAT for that | ||
| 97 | protocol. 8) --RR */ | ||
| 98 | if (!ct) | ||
| 99 | return NF_ACCEPT; | ||
| 100 | |||
| 101 | /* Don't try to NAT if this packet is not conntracked */ | ||
| 102 | if (nf_ct_is_untracked(ct)) | ||
| 103 | return NF_ACCEPT; | ||
| 104 | |||
| 105 | nat = nfct_nat(ct); | ||
| 106 | if (!nat) { | ||
| 107 | /* NAT module was loaded late. */ | ||
| 108 | if (nf_ct_is_confirmed(ct)) | ||
| 109 | return NF_ACCEPT; | ||
| 110 | nat = nf_ct_ext_add(ct, NF_CT_EXT_NAT, GFP_ATOMIC); | ||
| 111 | if (nat == NULL) { | ||
| 112 | pr_debug("failed to add NAT extension\n"); | ||
| 113 | return NF_ACCEPT; | ||
| 114 | } | ||
| 115 | } | ||
| 116 | |||
| 117 | switch (ctinfo) { | ||
| 118 | case IP_CT_RELATED: | ||
| 119 | case IP_CT_RELATED_REPLY: | ||
| 120 | if (ip_hdr(skb)->protocol == IPPROTO_ICMP) { | ||
| 121 | if (!nf_nat_icmp_reply_translation(ct, ctinfo, | ||
| 122 | hooknum, skb)) | ||
| 123 | return NF_DROP; | ||
| 124 | else | ||
| 125 | return NF_ACCEPT; | ||
| 126 | } | ||
| 127 | /* Fall thru... (Only ICMPs can be IP_CT_IS_REPLY) */ | ||
| 128 | case IP_CT_NEW: | ||
| 129 | |||
| 130 | /* Seen it before? This can happen for loopback, retrans, | ||
| 131 | or local packets.. */ | ||
| 132 | if (!nf_nat_initialized(ct, maniptype)) { | ||
| 133 | unsigned int ret; | ||
| 134 | |||
| 135 | ret = nf_nat_rule_find(skb, hooknum, in, out, ct); | ||
| 136 | if (ret != NF_ACCEPT) | ||
| 137 | return ret; | ||
| 138 | } else | ||
| 139 | pr_debug("Already setup manip %s for ct %p\n", | ||
| 140 | maniptype == IP_NAT_MANIP_SRC ? "SRC" : "DST", | ||
| 141 | ct); | ||
| 142 | break; | ||
| 143 | |||
| 144 | default: | ||
| 145 | /* ESTABLISHED */ | ||
| 146 | NF_CT_ASSERT(ctinfo == IP_CT_ESTABLISHED || | ||
| 147 | ctinfo == IP_CT_ESTABLISHED_REPLY); | ||
| 148 | } | ||
| 149 | |||
| 150 | return nf_nat_packet(ct, ctinfo, hooknum, skb); | ||
| 151 | } | ||
| 152 | |||
| 153 | static unsigned int | ||
| 154 | nf_nat_in(unsigned int hooknum, | ||
| 155 | struct sk_buff *skb, | ||
| 156 | const struct net_device *in, | ||
| 157 | const struct net_device *out, | ||
| 158 | int (*okfn)(struct sk_buff *)) | ||
| 159 | { | ||
| 160 | unsigned int ret; | ||
| 161 | __be32 daddr = ip_hdr(skb)->daddr; | ||
| 162 | |||
| 163 | ret = nf_nat_fn(hooknum, skb, in, out, okfn); | ||
| 164 | if (ret != NF_DROP && ret != NF_STOLEN && | ||
| 165 | daddr != ip_hdr(skb)->daddr) | ||
| 166 | skb_dst_drop(skb); | ||
| 167 | |||
| 168 | return ret; | ||
| 169 | } | ||
| 170 | |||
| 171 | static unsigned int | ||
| 172 | nf_nat_out(unsigned int hooknum, | ||
| 173 | struct sk_buff *skb, | ||
| 174 | const struct net_device *in, | ||
| 175 | const struct net_device *out, | ||
| 176 | int (*okfn)(struct sk_buff *)) | ||
| 177 | { | ||
| 178 | #ifdef CONFIG_XFRM | ||
| 179 | const struct nf_conn *ct; | ||
| 180 | enum ip_conntrack_info ctinfo; | ||
| 181 | #endif | ||
| 182 | unsigned int ret; | ||
| 183 | |||
| 184 | /* root is playing with raw sockets. */ | ||
| 185 | if (skb->len < sizeof(struct iphdr) || | ||
| 186 | ip_hdrlen(skb) < sizeof(struct iphdr)) | ||
| 187 | return NF_ACCEPT; | ||
| 188 | |||
| 189 | ret = nf_nat_fn(hooknum, skb, in, out, okfn); | ||
| 190 | #ifdef CONFIG_XFRM | ||
| 191 | if (ret != NF_DROP && ret != NF_STOLEN && | ||
| 192 | (ct = nf_ct_get(skb, &ctinfo)) != NULL) { | ||
| 193 | enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); | ||
| 194 | |||
| 195 | if ((ct->tuplehash[dir].tuple.src.u3.ip != | ||
| 196 | ct->tuplehash[!dir].tuple.dst.u3.ip) || | ||
| 197 | (ct->tuplehash[dir].tuple.src.u.all != | ||
| 198 | ct->tuplehash[!dir].tuple.dst.u.all) | ||
| 199 | ) | ||
| 200 | return ip_xfrm_me_harder(skb) == 0 ? ret : NF_DROP; | ||
| 201 | } | ||
| 202 | #endif | ||
| 203 | return ret; | ||
| 204 | } | ||
| 205 | |||
| 206 | static unsigned int | ||
| 207 | nf_nat_local_fn(unsigned int hooknum, | ||
| 208 | struct sk_buff *skb, | ||
| 209 | const struct net_device *in, | ||
| 210 | const struct net_device *out, | ||
| 211 | int (*okfn)(struct sk_buff *)) | ||
| 212 | { | ||
| 213 | const struct nf_conn *ct; | ||
| 214 | enum ip_conntrack_info ctinfo; | ||
| 215 | unsigned int ret; | ||
| 216 | |||
| 217 | /* root is playing with raw sockets. */ | ||
| 218 | if (skb->len < sizeof(struct iphdr) || | ||
| 219 | ip_hdrlen(skb) < sizeof(struct iphdr)) | ||
| 220 | return NF_ACCEPT; | ||
| 221 | |||
| 222 | ret = nf_nat_fn(hooknum, skb, in, out, okfn); | ||
| 223 | if (ret != NF_DROP && ret != NF_STOLEN && | ||
| 224 | (ct = nf_ct_get(skb, &ctinfo)) != NULL) { | ||
| 225 | enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); | ||
| 226 | |||
| 227 | if (ct->tuplehash[dir].tuple.dst.u3.ip != | ||
| 228 | ct->tuplehash[!dir].tuple.src.u3.ip) { | ||
| 229 | if (ip_route_me_harder(skb, RTN_UNSPEC)) | ||
| 230 | ret = NF_DROP; | ||
| 231 | } | ||
| 232 | #ifdef CONFIG_XFRM | ||
| 233 | else if (ct->tuplehash[dir].tuple.dst.u.all != | ||
| 234 | ct->tuplehash[!dir].tuple.src.u.all) | ||
| 235 | if (ip_xfrm_me_harder(skb)) | ||
| 236 | ret = NF_DROP; | ||
| 237 | #endif | ||
| 238 | } | ||
| 239 | return ret; | ||
| 240 | } | ||
| 241 | |||
| 242 | /* We must be after connection tracking and before packet filtering. */ | ||
| 243 | |||
| 244 | static struct nf_hook_ops nf_nat_ops[] __read_mostly = { | ||
| 245 | /* Before packet filtering, change destination */ | ||
| 246 | { | ||
| 247 | .hook = nf_nat_in, | ||
| 248 | .owner = THIS_MODULE, | ||
| 249 | .pf = NFPROTO_IPV4, | ||
| 250 | .hooknum = NF_INET_PRE_ROUTING, | ||
| 251 | .priority = NF_IP_PRI_NAT_DST, | ||
| 252 | }, | ||
| 253 | /* After packet filtering, change source */ | ||
| 254 | { | ||
| 255 | .hook = nf_nat_out, | ||
| 256 | .owner = THIS_MODULE, | ||
| 257 | .pf = NFPROTO_IPV4, | ||
| 258 | .hooknum = NF_INET_POST_ROUTING, | ||
| 259 | .priority = NF_IP_PRI_NAT_SRC, | ||
| 260 | }, | ||
| 261 | /* Before packet filtering, change destination */ | ||
| 262 | { | ||
| 263 | .hook = nf_nat_local_fn, | ||
| 264 | .owner = THIS_MODULE, | ||
| 265 | .pf = NFPROTO_IPV4, | ||
| 266 | .hooknum = NF_INET_LOCAL_OUT, | ||
| 267 | .priority = NF_IP_PRI_NAT_DST, | ||
| 268 | }, | ||
| 269 | /* After packet filtering, change source */ | ||
| 270 | { | ||
| 271 | .hook = nf_nat_fn, | ||
| 272 | .owner = THIS_MODULE, | ||
| 273 | .pf = NFPROTO_IPV4, | ||
| 274 | .hooknum = NF_INET_LOCAL_IN, | ||
| 275 | .priority = NF_IP_PRI_NAT_SRC, | ||
| 276 | }, | ||
| 277 | }; | ||
| 278 | |||
| 279 | static int __init nf_nat_standalone_init(void) | ||
| 280 | { | ||
| 281 | int ret = 0; | ||
| 282 | |||
| 283 | need_ipv4_conntrack(); | ||
| 284 | |||
| 285 | #ifdef CONFIG_XFRM | ||
| 286 | BUG_ON(ip_nat_decode_session != NULL); | ||
| 287 | rcu_assign_pointer(ip_nat_decode_session, nat_decode_session); | ||
| 288 | #endif | ||
| 289 | ret = nf_nat_rule_init(); | ||
| 290 | if (ret < 0) { | ||
| 291 | pr_err("nf_nat_init: can't setup rules.\n"); | ||
| 292 | goto cleanup_decode_session; | ||
| 293 | } | ||
| 294 | ret = nf_register_hooks(nf_nat_ops, ARRAY_SIZE(nf_nat_ops)); | ||
| 295 | if (ret < 0) { | ||
| 296 | pr_err("nf_nat_init: can't register hooks.\n"); | ||
| 297 | goto cleanup_rule_init; | ||
| 298 | } | ||
| 299 | return ret; | ||
| 300 | |||
| 301 | cleanup_rule_init: | ||
| 302 | nf_nat_rule_cleanup(); | ||
| 303 | cleanup_decode_session: | ||
| 304 | #ifdef CONFIG_XFRM | ||
| 305 | rcu_assign_pointer(ip_nat_decode_session, NULL); | ||
| 306 | synchronize_net(); | ||
| 307 | #endif | ||
| 308 | return ret; | ||
| 309 | } | ||
| 310 | |||
| 311 | static void __exit nf_nat_standalone_fini(void) | ||
| 312 | { | ||
| 313 | nf_unregister_hooks(nf_nat_ops, ARRAY_SIZE(nf_nat_ops)); | ||
| 314 | nf_nat_rule_cleanup(); | ||
| 315 | #ifdef CONFIG_XFRM | ||
| 316 | rcu_assign_pointer(ip_nat_decode_session, NULL); | ||
| 317 | synchronize_net(); | ||
| 318 | #endif | ||
| 319 | /* Conntrack caches are unregistered in nf_conntrack_cleanup */ | ||
| 320 | } | ||
| 321 | |||
| 322 | module_init(nf_nat_standalone_init); | ||
| 323 | module_exit(nf_nat_standalone_fini); | ||
| 324 | |||
| 325 | MODULE_LICENSE("GPL"); | ||
| 326 | MODULE_ALIAS("ip_nat"); | ||
diff --git a/net/ipv4/netfilter/nf_nat_tftp.c b/net/ipv4/netfilter/nf_nat_tftp.c new file mode 100644 index 00000000000..7274a43c7a1 --- /dev/null +++ b/net/ipv4/netfilter/nf_nat_tftp.c | |||
| @@ -0,0 +1,51 @@ | |||
| 1 | /* (C) 2001-2002 Magnus Boden <mb@ozaba.mine.nu> | ||
| 2 | * | ||
| 3 | * This program is free software; you can redistribute it and/or modify | ||
| 4 | * it under the terms of the GNU General Public License version 2 as | ||
| 5 | * published by the Free Software Foundation. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #include <linux/module.h> | ||
| 9 | #include <linux/udp.h> | ||
| 10 | |||
| 11 | #include <net/netfilter/nf_nat_helper.h> | ||
| 12 | #include <net/netfilter/nf_nat_rule.h> | ||
| 13 | #include <net/netfilter/nf_conntrack_helper.h> | ||
| 14 | #include <net/netfilter/nf_conntrack_expect.h> | ||
| 15 | #include <linux/netfilter/nf_conntrack_tftp.h> | ||
| 16 | |||
| 17 | MODULE_AUTHOR("Magnus Boden <mb@ozaba.mine.nu>"); | ||
| 18 | MODULE_DESCRIPTION("TFTP NAT helper"); | ||
| 19 | MODULE_LICENSE("GPL"); | ||
| 20 | MODULE_ALIAS("ip_nat_tftp"); | ||
| 21 | |||
| 22 | static unsigned int help(struct sk_buff *skb, | ||
| 23 | enum ip_conntrack_info ctinfo, | ||
| 24 | struct nf_conntrack_expect *exp) | ||
| 25 | { | ||
| 26 | const struct nf_conn *ct = exp->master; | ||
| 27 | |||
| 28 | exp->saved_proto.udp.port | ||
| 29 | = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.udp.port; | ||
| 30 | exp->dir = IP_CT_DIR_REPLY; | ||
| 31 | exp->expectfn = nf_nat_follow_master; | ||
| 32 | if (nf_ct_expect_related(exp) != 0) | ||
| 33 | return NF_DROP; | ||
| 34 | return NF_ACCEPT; | ||
| 35 | } | ||
| 36 | |||
| 37 | static void __exit nf_nat_tftp_fini(void) | ||
| 38 | { | ||
| 39 | rcu_assign_pointer(nf_nat_tftp_hook, NULL); | ||
| 40 | synchronize_rcu(); | ||
| 41 | } | ||
| 42 | |||
| 43 | static int __init nf_nat_tftp_init(void) | ||
| 44 | { | ||
| 45 | BUG_ON(nf_nat_tftp_hook != NULL); | ||
| 46 | rcu_assign_pointer(nf_nat_tftp_hook, help); | ||
| 47 | return 0; | ||
| 48 | } | ||
| 49 | |||
| 50 | module_init(nf_nat_tftp_init); | ||
| 51 | module_exit(nf_nat_tftp_fini); | ||
