/* Returns verdict for packet, or -1 for invalid. */ static int gre_packet(struct ip_conntrack *conntrack, struct iphdr *iph, size_t len, enum ip_conntrack_info ctinfo) { #ifdef CONFIG_IP_NF_PPTP_DEBUG struct pptp_gre_hdr *greh = (struct pptp_gre_hdr *)((u_int32_t *)iph + iph->ihl); #endif /* * If we've seen traffic both ways, this is a connected GRE stream. * Extend timeout. */ if (conntrack->status & IPS_SEEN_REPLY) { ip_ct_refresh(conntrack, GRE_CONNECTED_TIMEOUT); /* Also, more likely to be important, and not a probe */ set_bit(IPS_ASSURED_BIT, &conntrack->status); } else ip_ct_refresh(conntrack, GRE_TIMEOUT); DEBUGP("CT=%lx, Master=%lx, DIR=%s ", (unsigned long) conntrack, (unsigned long) conntrack->help.ct_pptp_info.master, (ctinfo >= IP_CT_IS_REPLY ? "reply " : "original")); PRINTK_GRE_HDR("", iph, greh); return NF_ACCEPT; }
static void delete_connection(struct ip_conntrack *ct, enum ip_conntrack_info ctinfo) { clear_gre_tuples(ct); ip_ct_refresh(ct, 5*HZ); //nf_conntrack_put(&ct->master); #if 0 /* expire the GRE connection */ l = &ct->help.ct_pptp_info.list; for (e=l->next; e!=l; e=e->next) { ct_gre = list_entry(e, struct ip_conntrack, help.ct_pptp_info.list); if(!ct_gre) { DEBUGP("What - NULL ct_gre!\n"); continue; } list_del(&ct_gre->help.ct_pptp_info.list); ip_ct_refresh(ct_gre, 5*HZ); if (del_timer(&ct_gre->timeout)) ct_gre->timeout.function((unsigned long)ct_gre); else DEBUGP("Couldn't delete GRE timer!\n"); } #endif }
/* Returns verdict for packet, and may modify conntracktype */ static int esp_packet(struct ip_conntrack *conntrack, const struct sk_buff *skb, enum ip_conntrack_info conntrackinfo) { const struct iphdr *iph = skb->nh.iph; const struct esphdr *esph = (void *)iph + iph->ihl*4; struct _esp_table *esp_entry; //printk ( KERN_DEBUG "%s:%s (0x%x) %u.%u.%u.%u <-> %u.%u.%u.%u %s\n", // __FILE__, __FUNCTION__, ntohl(esph->spi), // NIPQUAD(iph->saddr), NIPQUAD(iph->daddr), // (conntrackinfo == IP_CT_NEW ) ? "CT_NEW" : "SEEN_REPLY" ); /* * This should not happen. We get into this routine only if there is * an existing stream. */ if (conntrackinfo == IP_CT_NEW ) { //printk ( KERN_DEBUG "%s:%s IP_CT_NEW (0x%x) %u.%u.%u.%u <-> %u.%u.%u.%u\n", // __FILE__, __FUNCTION__, esph->spi, NIPQUAD(iph->saddr), NIPQUAD(iph->daddr)); if ( (esp_entry = search_esp_entry_by_spi ( esph, iph->daddr ) ) == NULL ) { esp_entry = alloc_esp_entry (); if ( esp_entry == NULL ) { /* All entries are currently in use */ //printk ( KERN_DEBUG "%s:%s All connections in use\n", // __FILE__, __FUNCTION__); return NF_DROP; } esp_entry->l_spi = ntohl(esph->spi); esp_entry->l_ip = ntohl(iph->saddr); esp_entry->r_spi = 0; } } /* If we've seen traffic both ways, this is some kind of UDP stream. Extend timeout. */ if (conntrack->status & IPS_SEEN_REPLY) { ip_ct_refresh(conntrack, ESP_STREAM_TIMEOUT); /* Also, more likely to be important, and not a probe */ set_bit(IPS_ASSURED_BIT, &conntrack->status); } else { ip_ct_refresh(conntrack, ESP_TIMEOUT); } //esp_entry = search_esp_entry_by_spi ( esph, iph->daddr ); if ( esp_entry != NULL ) { //printk ( KERN_DEBUG "%s:%s can modify this %u.%u.%u.%u" // " with %u.%u.%u.%u\n", // __FILE__, __FUNCTION__, // NIPQUAD(conntrack->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip), // NIPQUAD(esp_entry->l_ip) ); } return NF_ACCEPT; }
/* Returns verdict for packet, and may modify conntracktype */ static int esp_packet(struct ip_conntrack *conntrack, struct iphdr *iph, size_t len, enum ip_conntrack_info conntrackinfo) { /* If we've seen traffic both ways, this is some kind of UDP stream. Extend timeout. */ if (test_bit(IPS_SEEN_REPLY_BIT, &conntrack->status)) { ip_ct_refresh(conntrack, ip_ct_esp_timeout_stream); /* Also, more likely to be important, and not a probe */ set_bit(IPS_ASSURED_BIT, &conntrack->status); } else ip_ct_refresh(conntrack, ip_ct_esp_timeout); return NF_ACCEPT; }
/* Returns verdict for packet, or -1 for invalid. */ static int tcp_packet(struct ip_conntrack *conntrack, struct iphdr *iph, size_t len, enum ip_conntrack_info ctinfo) { enum tcp_conntrack newconntrack, oldtcpstate; struct tcphdr *tcph = (struct tcphdr *)((u_int32_t *)iph + iph->ihl); /* We're guaranteed to have the base header, but maybe not the options. */ if (len < (iph->ihl + tcph->doff) * 4) { DEBUGP("ip_conntrack_tcp: Truncated packet.\n"); return -1; } WRITE_LOCK(&tcp_lock); oldtcpstate = conntrack->proto.tcp.state; newconntrack = tcp_conntracks [CTINFO2DIR(ctinfo)] [get_conntrack_index(tcph)][oldtcpstate]; /* Invalid */ if (newconntrack == TCP_CONNTRACK_MAX) { DEBUGP("ip_conntrack_tcp: Invalid dir=%i index=%u conntrack=%u\n", CTINFO2DIR(ctinfo), get_conntrack_index(tcph), conntrack->proto.tcp.state); WRITE_UNLOCK(&tcp_lock); return -1; } conntrack->proto.tcp.state = newconntrack; /* Poor man's window tracking: record SYN/ACK for handshake check */ if (oldtcpstate == TCP_CONNTRACK_SYN_SENT && CTINFO2DIR(ctinfo) == IP_CT_DIR_REPLY && tcph->syn && tcph->ack) conntrack->proto.tcp.handshake_ack = htonl(ntohl(tcph->seq) + 1); WRITE_UNLOCK(&tcp_lock); /* If only reply is a RST, we can consider ourselves not to have an established connection: this is a fairly common problem case, so we can delete the conntrack immediately. --RR */ if (!(conntrack->status & IPS_SEEN_REPLY) && tcph->rst) { if (del_timer(&conntrack->timeout)) conntrack->timeout.function((unsigned long)conntrack); } else { /* Set ASSURED if we see see valid ack in ESTABLISHED after SYN_RECV */ if (oldtcpstate == TCP_CONNTRACK_SYN_RECV && CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL && tcph->ack && !tcph->syn && tcph->ack_seq == conntrack->proto.tcp.handshake_ack) set_bit(IPS_ASSURED_BIT, &conntrack->status); ip_ct_refresh(conntrack, tcp_timeouts[newconntrack]); } return NF_ACCEPT; }
/* timeout GRE data connections */ static int pptp_timeout_related(struct ip_conntrack *ct) { struct list_head *cur_item, *next; struct ip_conntrack_expect *exp; /* FIXME: do we have to lock something ? */ for (cur_item = ct->sibling_list.next; cur_item != &ct->sibling_list; cur_item = next) { next = cur_item->next; exp = list_entry(cur_item, struct ip_conntrack_expect, expected_list); ip_ct_gre_keymap_destroy(exp); if (!exp->sibling) { ip_conntrack_unexpect_related(exp); continue; } DEBUGP("setting timeout of conntrack %p to 0\n", exp->sibling); exp->sibling->proto.gre.timeout = 0; exp->sibling->proto.gre.stream_timeout = 0; ip_ct_refresh(exp->sibling, 0); } return 0; }
/* Returns verdict for packet, or -1 for invalid. */ static int established(struct ip_conntrack *conntrack, struct iphdr *iph, size_t len, enum ip_conntrack_info conntrackinfo) { ip_ct_refresh(conntrack, ip_ct_generic_timeout); return NF_ACCEPT; }
/* Returns verdict for packet, and may modify conntracktype */ static int udp_packet(struct ip_conntrack *conntrack, const struct sk_buff *skb, enum ip_conntrack_info conntrackinfo) { /* If we've seen traffic both ways, this is some kind of UDP stream. Extend timeout. */ if (test_bit(IPS_SEEN_REPLY_BIT, &conntrack->status)) { ip_ct_refresh(conntrack, ip_ct_udp_timeout_stream); /* Also, more likely to be important, and not a probe */ set_bit(IPS_ASSURED_BIT, &conntrack->status); } else #if defined(CONFIG_MIPS_BRCM) { /* Special handling of UNRPLIED DNS query packet: Song Wang * Before NAT and WAN interface are UP, during that time window, * if a DNS query is sent out, there will be an UNRPLIED DNS connection track entry * in which expected src/dst are private IP addresses in the tuple. * After NAT and WAN interface are UP, the UNRPLIED DNS connection track * entry should go away ASAP to enable the establishment of the tuple with * the expected src/dst that are public IP addresses. */ struct iphdr *iph = skb->nh.iph; struct udphdr *udph = (void *)iph + iph->ihl * 4; __u16 dport = ntohs(udph->dest); if (dport == 53) ip_ct_refresh(conntrack, ip_ct_udp_unreplieddns_timeout); else ip_ct_refresh(conntrack, ip_ct_udp_timeout); } #else ip_ct_refresh(conntrack, ip_ct_udp_timeout); #endif return NF_ACCEPT; }
static int ip_outbound_pptp_tcp(const struct iphdr *iph, struct ip_conntrack *ct, enum ip_conntrack_info ctinfo) { struct pptp_pkt_hdr *pptph; struct PptpControlHeader *ctlh; union { void *rawreq; struct PptpOutCallRequest *ocreq; struct PptpOutCallReply *ocack; struct PptpInCallRequest *icreq; struct PptpInCallReply *icack; struct PptpClearCallRequest *clrreq; struct PptpCallDisconnectNotify *disc; struct PptpWanErrorNotify *wanerr; struct PptpSetLinkInfo *setlink; } pptpReq; __u16 msg, *cid; pptph = (struct pptp_pkt_hdr *) ((char *) iph + sizeof(struct iphdr) + sizeof(struct tcphdr)); DEBUGP("outbound_pptp_tcp(): CT=%lx, ", (unsigned long) ct); PRINTK_PPTP_HDR("", iph, pptph); ctlh = (struct PptpControlHeader *) ((char *) pptph + sizeof(struct pptp_pkt_hdr)); pptpReq.rawreq = (void *) ((char*) ctlh + sizeof(struct PptpControlHeader)); switch (msg = htons(ctlh->messageType)) { case PPTP_OUT_CALL_REQUEST: /* masq'd client initiating connection to server */ cid = &pptpReq.ocreq->callID; break; /* create conntrack and get CID */ case PPTP_IN_CALL_REQUEST: /* masq'd client initiating connection to server */ cid = &pptpReq.icreq->callID; break; /* create conntrack and get CID */ case PPTP_CALL_CLEAR_REQUEST: /* masq'd client sending to server */ /* no need to alter conntrack */ return 0; case PPTP_CALL_DISCONNECT_NOTIFY: /* masq'd client notifying server */ /* expire this connection */ ip_ct_refresh(ct, (30*HZ)); clear_gre_tuples(ct); return 0; default: DEBUGP("UNKNOWN outbound packet: "); DEBUGP("%s (TY=%d)\n", (msg <= PPTP_MSG_MAX)? strMName[msg]:strMName[0], msg); /* fall through */ case PPTP_SET_LINK_INFO: case PPTP_START_SESSION_REQUEST: case PPTP_START_SESSION_REPLY: case PPTP_STOP_SESSION_REQUEST: case PPTP_STOP_SESSION_REPLY: case PPTP_ECHO_REQUEST: /* no need to alter conntrack */ return 0; } /* Info for NAT */ DEBUGP("ip_outbound_pptp_tcp: original client call id: %d\n", *cid); ct->nat.help.pptp_info.call_id = *cid; DEBUGP("ip_outbound_pptp_tcp(): "); DEBUGP("%s, CT=%lx, CID=%d\n", strMName[msg], (unsigned long) ct, (cid ? ntohs(*cid) : 0)); return NF_ACCEPT; }
static int ip_inbound_pptp_tcp(const struct iphdr *iph, struct ip_conntrack *ct, enum ip_conntrack_info ctinfo) { struct pptp_pkt_hdr *pptph; struct PptpControlHeader *ctlh; union { void *rawreq; struct PptpOutCallRequest *ocreq; struct PptpOutCallReply *ocack; struct PptpInCallRequest *icreq; struct PptpInCallReply *icack; struct PptpClearCallRequest *clrreq; struct PptpCallDisconnectNotify *disc; struct PptpWanErrorNotify *wanerr; struct PptpSetLinkInfo *setlink; } pptpReq; __u16 msg, *cid, *pcid; int dir = CTINFO2DIR(ctinfo); pptph = (struct pptp_pkt_hdr *) ((char *) iph + sizeof(struct iphdr) + sizeof(struct tcphdr)); DEBUGP("inbound_pptp_tcp(): CT=%lx, ", (unsigned long) ct); PRINTK_PPTP_HDR("", iph, pptph); ctlh = (struct PptpControlHeader *) ((char *) pptph + sizeof(struct pptp_pkt_hdr)); pptpReq.rawreq = (void *) ((char*) ctlh + sizeof(struct PptpControlHeader)); switch (msg = htons(ctlh->messageType)) { case PPTP_OUT_CALL_REPLY: /* server responding to masq'd client */ cid = &pptpReq.ocack->callID; pcid = &pptpReq.ocack->peersCallID; break; case PPTP_IN_CALL_REPLY: /* server responding to masq'd client */ cid = &pptpReq.icack->callID; pcid = &pptpReq.icack->peersCallID; break; case PPTP_WAN_ERROR_NOTIFY: /* server notifying masq'd client */ /* no need to alter conntrack */ return 0; case PPTP_SET_LINK_INFO: /* server notifying masq'd client */ /* no need to alter conntrack */ return 0; case PPTP_CALL_DISCONNECT_NOTIFY: /* server notifying masq'd client */ /* expire this connection */ ip_ct_refresh(ct, (30*HZ)); clear_gre_tuples(ct); return 0; default: DEBUGP("UNKNOWN inbound packet: "); DEBUGP("%s (TY=%d)\n", (msg <= PPTP_MSG_MAX)? strMName[msg] : strMName[0], msg); /* fall through */ case PPTP_ECHO_REPLY: case PPTP_START_SESSION_REQUEST: case PPTP_START_SESSION_REPLY: case PPTP_STOP_SESSION_REQUEST: case PPTP_STOP_SESSION_REPLY: case PPTP_ECHO_REQUEST: /* no need to alter conntrack */ return 0; } LOCK_BH(&ip_pptp_lock); /* info for conntrack/NAT */ struct ip_ct_pptp_master *ct_pptp_info = &ct->help.ct_pptp_info; struct ip_conntrack_expect expect, *exp = &expect; struct ip_ct_pptp_expect *exp_pptp_info = &exp->help.exp_pptp_info; exp_pptp_info->pptp_magic = PPTP_TCP_PORT; /* our magic number */ exp_pptp_info->orig_call_id = *cid; exp_pptp_info->peer_call_id = *pcid; INIT_LIST_HEAD(&(ct_pptp_info->list)); /* tuple for GRE packets (from server to masqed client) * Here src = pptp server, dst = ppp addr * !dir: src = masq client, dst = pptp server */ /* * masq client <--> pptp serv * new connection replaces any old ones. */ /* * populate our lists for peer call ID lookup */ put_gre_tuple(ct->tuplehash[!dir].tuple.src.ip, ct->tuplehash[!dir].tuple.dst.ip, *cid, *pcid, ct); put_gre_tuple(ct->tuplehash[!dir].tuple.dst.ip, ct->tuplehash[!dir].tuple.src.ip, *pcid, *cid, ct); put_gre_tuple(ct->tuplehash[dir].tuple.src.ip, ct->tuplehash[dir].tuple.dst.ip, *pcid, *cid, ct); put_gre_tuple(ct->tuplehash[dir].tuple.dst.ip, ct->tuplehash[dir].tuple.src.ip, *cid, *pcid, ct); if(ip_conntrack_protocol_register(&ip_conntrack_protocol_gre) == 0) DEBUGP("pptp: registered conntrack protocol GRE!\n"); else DEBUGP("pptp: failed to register conntrack protocol GRE!\n"); UNLOCK_BH(&ip_pptp_lock); return 0; }
static int help(struct sk_buff **pskb, struct ip_conntrack *ct, enum ip_conntrack_info ctinfo) { struct ip_conntrack_expect *exp; char *data, *data_limit, *tmp; unsigned int dataoff, i; u_int16_t port, len; int ret = NF_ACCEPT; /* Only look at packets from the Amanda server */ if (CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL) return NF_ACCEPT; /* increase the UDP timeout of the master connection as replies from * Amanda clients to the server can be quite delayed */ ip_ct_refresh(ct, *pskb, master_timeout * HZ); /* No data? */ dataoff = (*pskb)->nh.iph->ihl*4 + sizeof(struct udphdr); if (dataoff >= (*pskb)->len) { if (net_ratelimit()) printk("amanda_help: skblen = %u\n", (*pskb)->len); return NF_ACCEPT; } spin_lock_bh(&amanda_buffer_lock); skb_copy_bits(*pskb, dataoff, amanda_buffer, (*pskb)->len - dataoff); data = amanda_buffer; data_limit = amanda_buffer + (*pskb)->len - dataoff; *data_limit = '\0'; /* Search for the CONNECT string */ data = strstr(data, "CONNECT "); if (!data) goto out; data += strlen("CONNECT "); /* Only search first line. */ if ((tmp = strchr(data, '\n'))) *tmp = '\0'; for (i = 0; i < ARRAY_SIZE(conns); i++) { char *match = strstr(data, conns[i]); if (!match) continue; tmp = data = match + strlen(conns[i]); port = simple_strtoul(data, &data, 10); len = data - tmp; if (port == 0 || len > 5) break; exp = ip_conntrack_expect_alloc(ct); if (exp == NULL) { ret = NF_DROP; goto out; } exp->expectfn = NULL; exp->flags = 0; exp->tuple.src.ip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip; exp->tuple.src.u.tcp.port = 0; exp->tuple.dst.ip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip; exp->tuple.dst.protonum = IPPROTO_TCP; exp->tuple.dst.u.tcp.port = htons(port); exp->mask.src.ip = 0xFFFFFFFF; exp->mask.src.u.tcp.port = 0; exp->mask.dst.ip = 0xFFFFFFFF; exp->mask.dst.protonum = 0xFF; exp->mask.dst.u.tcp.port = 0xFFFF; if (ip_nat_amanda_hook) ret = ip_nat_amanda_hook(pskb, ctinfo, tmp - amanda_buffer, len, exp); else if (ip_conntrack_expect_related(exp) != 0) ret = NF_DROP; ip_conntrack_expect_put(exp); } out: spin_unlock_bh(&amanda_buffer_lock); return ret; }
/* Returns verdict for packet, or -1 for invalid. */ static int tcp_packet(struct ip_conntrack *conntrack, const struct sk_buff *skb, enum ip_conntrack_info ctinfo) { enum tcp_conntrack newconntrack, oldtcpstate; struct tcphdr tcph; if (skb_copy_bits(skb, skb->nh.iph->ihl * 4, &tcph, sizeof(tcph)) != 0) return -1; if (skb->len < skb->nh.iph->ihl * 4 + tcph.doff * 4) return -1; /* If only reply is a RST, we can consider ourselves not to have an established connection: this is a fairly common problem case, so we can delete the conntrack immediately. --RR */ if (!test_bit(IPS_SEEN_REPLY_BIT, &conntrack->status) && tcph.rst) { if (del_timer(&conntrack->timeout)) conntrack->timeout.function((unsigned long)conntrack); return NF_ACCEPT; } WRITE_LOCK(&tcp_lock); oldtcpstate = conntrack->proto.tcp.state; newconntrack = tcp_conntracks [CTINFO2DIR(ctinfo)] [get_conntrack_index(&tcph)][oldtcpstate]; /* Invalid */ if (newconntrack == TCP_CONNTRACK_MAX) { DEBUGP("ip_conntrack_tcp: Invalid dir=%i index=%u conntrack=%u\n", CTINFO2DIR(ctinfo), get_conntrack_index(&tcph), conntrack->proto.tcp.state); WRITE_UNLOCK(&tcp_lock); return -1; } conntrack->proto.tcp.state = newconntrack; /* Poor man's window tracking: record SYN/ACK for handshake check */ if (oldtcpstate == TCP_CONNTRACK_SYN_SENT && CTINFO2DIR(ctinfo) == IP_CT_DIR_REPLY && tcph.syn && tcph.ack) { conntrack->proto.tcp.handshake_ack = htonl(ntohl(tcph.seq) + 1); goto out; } /* Set ASSURED if we see valid ack in ESTABLISHED after SYN_RECV */ if (oldtcpstate == TCP_CONNTRACK_SYN_RECV && CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL && tcph.ack && !tcph.syn && tcph.ack_seq == conntrack->proto.tcp.handshake_ack) set_bit(IPS_ASSURED_BIT, &conntrack->status); out: WRITE_UNLOCK(&tcp_lock); ip_ct_refresh(conntrack, *tcp_timeouts[newconntrack]); return NF_ACCEPT; }
static int help(struct sk_buff **pskb, struct ip_conntrack *ct, enum ip_conntrack_info ctinfo) { struct ts_state ts; struct ip_conntrack_expect *exp; unsigned int dataoff, start, stop, off, i; char pbuf[sizeof("65535")], *tmp; u_int16_t port, len; int ret = NF_ACCEPT; /* Only look at packets from the Amanda server */ if (CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL) return NF_ACCEPT; /* increase the UDP timeout of the master connection as replies from * Amanda clients to the server can be quite delayed */ ip_ct_refresh(ct, *pskb, master_timeout * HZ); /* No data? */ dataoff = (*pskb)->nh.iph->ihl*4 + sizeof(struct udphdr); if (dataoff >= (*pskb)->len) { if (net_ratelimit()) printk("amanda_help: skblen = %u\n", (*pskb)->len); return NF_ACCEPT; } memset(&ts, 0, sizeof(ts)); start = skb_find_text(*pskb, dataoff, (*pskb)->len, search[SEARCH_CONNECT].ts, &ts); if (start == UINT_MAX) goto out; start += dataoff + search[SEARCH_CONNECT].len; memset(&ts, 0, sizeof(ts)); stop = skb_find_text(*pskb, start, (*pskb)->len, search[SEARCH_NEWLINE].ts, &ts); if (stop == UINT_MAX) goto out; stop += start; for (i = SEARCH_DATA; i <= SEARCH_INDEX; i++) { memset(&ts, 0, sizeof(ts)); off = skb_find_text(*pskb, start, stop, search[i].ts, &ts); if (off == UINT_MAX) continue; off += start + search[i].len; len = min_t(unsigned int, sizeof(pbuf) - 1, stop - off); if (skb_copy_bits(*pskb, off, pbuf, len)) break; pbuf[len] = '\0'; port = simple_strtoul(pbuf, &tmp, 10); len = tmp - pbuf; if (port == 0 || len > 5) break; exp = ip_conntrack_expect_alloc(ct); if (exp == NULL) { ret = NF_DROP; goto out; } exp->expectfn = NULL; exp->flags = 0; exp->tuple.src.ip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip; exp->tuple.src.u.tcp.port = 0; exp->tuple.dst.ip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip; exp->tuple.dst.protonum = IPPROTO_TCP; exp->tuple.dst.u.tcp.port = htons(port); exp->mask.src.ip = htonl(0xFFFFFFFF); exp->mask.src.u.tcp.port = 0; exp->mask.dst.ip = htonl(0xFFFFFFFF); exp->mask.dst.protonum = 0xFF; exp->mask.dst.u.tcp.port = htons(0xFFFF); if (ip_nat_amanda_hook) ret = ip_nat_amanda_hook(pskb, ctinfo, off - dataoff, len, exp); else if (ip_conntrack_expect_related(exp) != 0) ret = NF_DROP; ip_conntrack_expect_put(exp); } out: return ret; }
static int help(struct sk_buff *skb, struct ip_conntrack *ct, enum ip_conntrack_info ctinfo) { char *data, *data_limit; int dir = CTINFO2DIR(ctinfo); unsigned int dataoff, i; struct ip_ct_amanda *info = (struct ip_ct_amanda *)&ct->help.ct_ftp_info; /* Can't track connections formed before we registered */ if (!info) return NF_ACCEPT; /* increase the UDP timeout of the master connection as replies from * Amanda clients to the server can be quite delayed */ ip_ct_refresh(ct, master_timeout * HZ); /* If packet is coming from Amanda server */ if (dir == IP_CT_DIR_ORIGINAL) return NF_ACCEPT; /* No data? */ dataoff = skb->nh.iph->ihl*4 + sizeof(struct udphdr); if (dataoff >= skb->len) { if (net_ratelimit()) printk("ip_conntrack_amanda_help: skblen = %u\n", (unsigned)skb->len); return NF_ACCEPT; } LOCK_BH(&ip_amanda_lock); skb_copy_bits(skb, dataoff, amanda_buffer, skb->len - dataoff); data = amanda_buffer; data_limit = amanda_buffer + skb->len - dataoff; *data_limit = '\0'; /* Search for the CONNECT string */ data = strstr(data, "CONNECT "); if (!data) goto out; DEBUGP("ip_conntrack_amanda_help: CONNECT found in connection " "%u.%u.%u.%u:%u %u.%u.%u.%u:%u\n", NIPQUAD(iph->saddr), htons(udph->source), NIPQUAD(iph->daddr), htons(udph->dest)); data += strlen("CONNECT "); /* Only search first line. */ if (strchr(data, '\n')) *strchr(data, '\n') = '\0'; for (i = 0; i < ARRAY_SIZE(conns); i++) { char *match = strstr(data, conns[i]); if (match) { char *portchr; struct ip_conntrack_expect expect; struct ip_ct_amanda_expect *exp_amanda_info = &expect.help.exp_amanda_info; memset(&expect, 0, sizeof(expect)); data += strlen(conns[i]); /* this is not really tcp, but let's steal an * idea from a tcp stream helper :-) */ // XXX expect.seq = data - amanda_buffer; exp_amanda_info->offset = data - amanda_buffer; // XXX DEBUGP("expect.seq = %p - %p = %d\n", data, amanda_buffer, expect.seq); DEBUGP("exp_amanda_info->offset = %p - %p = %d\n", data, amanda_buffer, exp_amanda_info->offset); portchr = data; exp_amanda_info->port = simple_strtoul(data, &data,10); exp_amanda_info->len = data - portchr; /* eat whitespace */ while (*data == ' ') data++; DEBUGP("ip_conntrack_amanda_help: " "CONNECT %s request with port " "%u found\n", conns[i], exp_amanda_info->port); expect.tuple = ((struct ip_conntrack_tuple) { { ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip, { 0 } }, { ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip, { htons(exp_amanda_info->port) }, IPPROTO_TCP }});