/** * Assumes that "tuple" represents a IPv6-UDP or ICMP packet, and filters and updates based on it. * * This is RFC 6146, first halves of both sections 3.5.1 and 3.5.3. * * @param[in] skb tuple's packet. This is actually only used for error reporting. * @param[in] tuple summary of the packet Jool is currently translating. * @return VER_CONTINUE if everything went OK, VER_DROP otherwise. */ static verdict ipv6_simple(struct packet *pkt, struct tuple *tuple6) { struct bib_entry *bib; struct session_entry *session; int error; error = bibdb_get_or_create_ipv6(pkt, tuple6, &bib); if (error) { inc_stats(pkt, IPSTATS_MIB_INDISCARDS); return VERDICT_DROP; } log_bib(bib); error = sessiondb_get_or_create_ipv6(tuple6, bib, &session); if (error) { inc_stats(pkt, IPSTATS_MIB_INDISCARDS); bib_return(bib); return VERDICT_DROP; } log_session(session); session_return(session); bib_return(bib); return VERDICT_CONTINUE; }
/** * Attempts to find "tuple"'s BIB entry and returns it in "bib". * Assumes "tuple" represents a IPv4 packet. */ static int get_bib_ipv4(struct packet *pkt, struct tuple *tuple4, struct bib_entry **bib) { int error; error = bibdb_get(tuple4, bib); if (error) { if (error == -ESRCH) { log_debug("There is no BIB entry for the incoming IPv4 packet."); inc_stats(pkt, IPSTATS_MIB_INNOROUTES); } else { log_debug("Error code %d while finding a BIB entry for the incoming packet.", error); inc_stats(pkt, IPSTATS_MIB_INDISCARDS); icmp64_send(pkt, ICMPERR_ADDR_UNREACHABLE, 0); } return error; } if (config_get_addr_dependent_filtering() && !sessiondb_allow(tuple4)) { log_debug("Packet was blocked by address-dependent filtering."); icmp64_send(pkt, ICMPERR_FILTER, 0); inc_stats(pkt, IPSTATS_MIB_INDISCARDS); bib_return(*bib); return -EPERM; } return 0; }
int sent_xmsg_stats(User *usr, XMsg *x, char *name) { switch(x->type) { case XMSG_X: usr->xsent++; user_dirty(usr, "xsent"); update_stats(usr); inc_stats(STATS_XSENT); break; case XMSG_EMOTE: usr->esent++; user_dirty(usr, "esent"); update_stats(usr); inc_stats(STATS_ESENT); break; case XMSG_FEELING: usr->fsent++; user_dirty(usr, "fsent"); update_stats(usr); inc_stats(STATS_FSENT); break; case XMSG_QUESTION: usr->qsent++; user_dirty(usr, "qsent"); update_stats(usr); inc_stats(STATS_QSENT); break; case XMSG_ANSWER: usr->qansw++; user_dirty(usr, "qansw"); update_stats(usr); inc_stats(STATS_QANSW); break; default: ; } if (name != NULL) { /* add to talked_to list */ if (usr->talked_to.len <= 0) gstrcpy(&(usr->talked_to), name); else { if (!cstrfind(usr->talked_to.str, name, ',')) { gstrcat(&(usr->talked_to), ","); gstrcat(&(usr->talked_to), name); } } } return 0; }
static verdict ipv6_icmp_err(struct sk_buff *skb, struct tuple *tuple6) { struct ipv6hdr *inner_ipv6 = (struct ipv6hdr *) (icmp6_hdr(skb) + 1); struct hdr_iterator iterator = HDR_ITERATOR_INIT(inner_ipv6); struct udphdr *inner_udp; struct tcphdr *inner_tcp; struct icmp6hdr *inner_icmp; tuple6->src.addr6.l3 = inner_ipv6->daddr; tuple6->dst.addr6.l3 = inner_ipv6->saddr; hdr_iterator_last(&iterator); switch (iterator.hdr_type) { case NEXTHDR_UDP: inner_udp = iterator.data; tuple6->src.addr6.l4 = be16_to_cpu(inner_udp->dest); tuple6->dst.addr6.l4 = be16_to_cpu(inner_udp->source); tuple6->l4_proto = L4PROTO_UDP; break; case NEXTHDR_TCP: inner_tcp = iterator.data; tuple6->src.addr6.l4 = be16_to_cpu(inner_tcp->dest); tuple6->dst.addr6.l4 = be16_to_cpu(inner_tcp->source); tuple6->l4_proto = L4PROTO_TCP; break; case NEXTHDR_ICMP: inner_icmp = iterator.data; if (is_icmp6_error(inner_icmp->icmp6_type)) { log_debug("Packet is a ICMP error containing a ICMP error."); inc_stats(skb, IPSTATS_MIB_INHDRERRORS); return VER_DROP; } tuple6->src.addr6.l4 = be16_to_cpu(inner_icmp->icmp6_dataun.u_echo.identifier); tuple6->dst.addr6.l4 = tuple6->src.addr6.l4; tuple6->l4_proto = L4PROTO_ICMP; break; default: log_debug("Packet's inner packet is not UDP, TCP or ICMPv6 (%d).", iterator.hdr_type); inc_stats(skb, IPSTATS_MIB_INUNKNOWNPROTOS); return VER_DROP; } tuple6->l3_proto = L3PROTO_IPV6; return VER_CONTINUE; }
static verdict ipv4_icmp_err(struct sk_buff *skb, struct tuple *tuple4) { struct iphdr *inner_ipv4 = (struct iphdr *) (icmp_hdr(skb) + 1); struct udphdr *inner_udp; struct tcphdr *inner_tcp; struct icmphdr *inner_icmp; tuple4->src.addr4.l3.s_addr = inner_ipv4->daddr; tuple4->dst.addr4.l3.s_addr = inner_ipv4->saddr; switch (inner_ipv4->protocol) { case IPPROTO_UDP: inner_udp = ipv4_extract_l4_hdr(inner_ipv4); tuple4->src.addr4.l4 = be16_to_cpu(inner_udp->dest); tuple4->dst.addr4.l4 = be16_to_cpu(inner_udp->source); tuple4->l4_proto = L4PROTO_UDP; break; case IPPROTO_TCP: inner_tcp = ipv4_extract_l4_hdr(inner_ipv4); tuple4->src.addr4.l4 = be16_to_cpu(inner_tcp->dest); tuple4->dst.addr4.l4 = be16_to_cpu(inner_tcp->source); tuple4->l4_proto = L4PROTO_TCP; break; case IPPROTO_ICMP: inner_icmp = ipv4_extract_l4_hdr(inner_ipv4); if (is_icmp4_error(inner_icmp->type)) { log_debug("Packet is a ICMP error containing a ICMP error."); inc_stats(skb, IPSTATS_MIB_INHDRERRORS); return VER_DROP; } tuple4->src.addr4.l4 = be16_to_cpu(inner_icmp->un.echo.id); tuple4->dst.addr4.l4 = tuple4->src.addr4.l4; tuple4->l4_proto = L4PROTO_ICMP; break; default: log_debug("Packet's inner packet is not UDP, TCP or ICMP (%d)", inner_ipv4->protocol); inc_stats(skb, IPSTATS_MIB_INUNKNOWNPROTOS); return VER_DROP; } tuple4->l3_proto = L3PROTO_IPV4; return VER_CONTINUE; }
/** * Assumes that "tuple" represents a IPv4-UDP or ICMP packet, and filters and updates based on it. * * This is RFC 6146, second halves of both sections 3.5.1 and 3.5.3. * * @param[in] skb tuple's packet. This is actually only used for error reporting. * @param[in] tuple summary of the packet Jool is currently translating. * @return VER_CONTINUE if everything went OK, VER_DROP otherwise. */ static verdict ipv4_simple(struct packet *pkt, struct tuple *tuple4) { int error; struct bib_entry *bib; struct session_entry *session; error = get_bib_ipv4(pkt, tuple4, &bib); if (error == -ESRCH) return VERDICT_ACCEPT; else if (error) return VERDICT_DROP; log_bib(bib); error = sessiondb_get_or_create_ipv4(tuple4, bib, &session); if (error) { inc_stats(pkt, IPSTATS_MIB_INDISCARDS); bib_return(bib); return VERDICT_DROP; } log_session(session); session_return(session); bib_return(bib); return VERDICT_CONTINUE; }
static void xfrout_senddone(isc_task_t *task, isc_event_t *event) { isc_socketevent_t *sev = (isc_socketevent_t *)event; xfrout_ctx_t *xfr = (xfrout_ctx_t *)event->ev_arg; isc_result_t evresult = sev->result; UNUSED(task); INSIST(event->ev_type == ISC_SOCKEVENT_SENDDONE); isc_event_free(&event); xfr->sends--; INSIST(xfr->sends == 0); (void)isc_timer_touch(xfr->client->timer); if (xfr->shuttingdown == ISC_TRUE) { xfrout_maybe_destroy(xfr); } else if (evresult != ISC_R_SUCCESS) { xfrout_fail(xfr, evresult, "send"); } else if (xfr->end_of_stream == ISC_FALSE) { sendstream(xfr); } else { /* End of zone transfer stream. */ inc_stats(xfr->zone, dns_nsstatscounter_xfrdone); xfrout_log(xfr, ISC_LOG_INFO, "%s ended", xfr->mnemonic); ns_client_next(xfr->client, ISC_R_SUCCESS); xfrout_ctx_destroy(&xfr); } }
unsigned int core_4to6(struct sk_buff *skb) { struct packet pkt; struct iphdr *hdr = ip_hdr(skb); int error; if (config_get_is_disable()) return NF_ACCEPT; /* Translation is disabled; let the packet pass. */ if (is_blacklisted4(hdr->saddr) || is_blacklisted4(hdr->daddr)) return NF_ACCEPT; if (nat64_is_stateful()) { if (!pool4_contains(hdr->daddr) || pool6_is_empty()) return NF_ACCEPT; /* Not meant for translation; let the kernel handle it. */ } log_debug("==============================================="); log_debug("Catching IPv4 packet: %pI4->%pI4", &hdr->saddr, &hdr->daddr); error = pkt_init_ipv4(&pkt, skb); /* Reminder: This function might change pointers. */ if (error) return NF_DROP; error = validate_icmp4_csum(&pkt); if (error) { inc_stats(&pkt, IPSTATS_MIB_INHDRERRORS); return NF_DROP; } return core_common(&pkt); }
/* Note: User u must be locked */ int recv_XMsg(User *u, XMsg *x) { PList *pl; if ((pl = new_PList(x)) == NULL) return -1; x->refcount++; append_PList(&(u->recv_xmsgs), pl); switch(x->type) { case XMSG_X: u->xrecv++; user_dirty(u, "xrecv"); update_stats(u); inc_stats(STATS_XRECV); break; case XMSG_EMOTE: u->erecv++; user_dirty(u, "erecv"); update_stats(u); inc_stats(STATS_ERECV); break; case XMSG_FEELING: u->frecv++; user_dirty(u, "frecv"); update_stats(u); inc_stats(STATS_FRECV); break; default: ; } wakeup(u); return 0; }
/** * Filtering and updating done during the CLOSED state of the TCP state machine. * Part of RFC 6146 section 3.5.2.2. */ static verdict tcp_closed_state_handle(struct packet *pkt, struct tuple *tuple) { struct bib_entry *bib; verdict result; int error; switch (pkt_l3_proto(pkt)) { case L3PROTO_IPV6: if (pkt_tcp_hdr(pkt)->syn) { result = is_error(tcp_closed_v6_syn(pkt, tuple)) ? VERDICT_DROP : VERDICT_CONTINUE; goto syn_out; } break; case L3PROTO_IPV4: if (pkt_tcp_hdr(pkt)->syn) { result = tcp_closed_v4_syn(pkt, tuple); goto syn_out; } break; } error = bibdb_get(tuple, &bib); if (error) { log_debug("Closed state: Packet is not SYN and there is no BIB entry, so discarding. " "ERRcode %d", error); inc_stats(pkt, IPSTATS_MIB_INNOROUTES); return VERDICT_DROP; } bib_return(bib); return VERDICT_CONTINUE; syn_out: if (result == VERDICT_DROP) inc_stats(pkt, IPSTATS_MIB_INDISCARDS); return result; }
/** * Assumes that "tuple" represents a TCP packet, and filters and updates based on it. * Encapsulates the TCP state machine. * * This is RFC 6146 section 3.5.2. */ static verdict tcp(struct packet *pkt, struct tuple *tuple) { struct session_entry *session; int error; error = sessiondb_get(tuple, &session); if (error != 0 && error != -ESRCH) { log_debug("Error code %d while trying to find a TCP session.", error); inc_stats(pkt, IPSTATS_MIB_INDISCARDS); return VERDICT_DROP; } if (error == -ESRCH) return tcp_closed_state_handle(pkt, tuple); log_session(session); error = sessiondb_tcp_state_machine(pkt, session); session_return(session); if (error) { inc_stats(pkt, IPSTATS_MIB_INDISCARDS); return VERDICT_DROP; } return VERDICT_CONTINUE; }
unsigned int core_6to4(struct sk_buff *skb) { struct packet pkt; struct ipv6hdr *hdr = ipv6_hdr(skb); int error; if (config_get_is_disable()) return NF_ACCEPT; /* Translation is disabled; let the packet pass. */ if (is_blacklisted6(&hdr->saddr) || is_blacklisted6(&hdr->daddr)) return NF_ACCEPT; if (nat64_is_stateful()) { if ((!pool6_contains(&hdr->daddr) || pool4_is_empty())) return NF_ACCEPT; /* Not meant for translation; let the kernel handle it. */ } log_debug("==============================================="); log_debug("Catching IPv6 packet: %pI6c->%pI6c", &hdr->saddr, &hdr->daddr); error = pkt_init_ipv6(&pkt, skb); /* Reminder: This function might change pointers. */ if (error) return NF_DROP; if (nat64_is_stateful()) { verdict result = fragdb_handle(&pkt); if (result != VERDICT_CONTINUE) return (unsigned int) result; } error = validate_icmp6_csum(&pkt); if (error) { inc_stats(&pkt, IPSTATS_MIB_INHDRERRORS); return NF_DROP; } return core_common(&pkt); }
/** * Assumes that "tuple" represents a IPv4-UDP or ICMP packet, and filters and updates based on it. * * This is RFC 6146, second halves of both sections 3.5.1 and 3.5.3. * * @param[in] skb tuple's packet. This is actually only used for error reporting. * @param[in] tuple summary of the packet Jool is currently translating. * @return VER_CONTINUE if everything went OK, VER_DROP otherwise. */ static verdict ipv4_simple(struct sk_buff *skb, struct tuple *tuple4) { int error; struct bib_entry *bib; struct session_entry *session; error = get_bib_ipv4(skb, tuple4, &bib); if (error) return VER_DROP; log_bib(bib); error = sessiondb_get_or_create_ipv4(tuple4, bib, &session); if (error) { inc_stats(skb, IPSTATS_MIB_INDISCARDS); bib_return(bib); return VER_DROP; } log_session(session); session_return(session); bib_return(bib); return VER_CONTINUE; }
void ns_xfr_start(ns_client_t *client, dns_rdatatype_t reqtype) { isc_result_t result; dns_name_t *question_name; dns_rdataset_t *question_rdataset; dns_zone_t *zone = NULL, *raw = NULL, *mayberaw; dns_db_t *db = NULL; dns_dbversion_t *ver = NULL; dns_rdataclass_t question_class; rrstream_t *soa_stream = NULL; rrstream_t *data_stream = NULL; rrstream_t *stream = NULL; dns_difftuple_t *current_soa_tuple = NULL; dns_name_t *soa_name; dns_rdataset_t *soa_rdataset; dns_rdata_t soa_rdata = DNS_RDATA_INIT; isc_boolean_t have_soa = ISC_FALSE; const char *mnemonic = NULL; isc_mem_t *mctx = client->mctx; dns_message_t *request = client->message; xfrout_ctx_t *xfr = NULL; isc_quota_t *quota = NULL; dns_transfer_format_t format = client->view->transfer_format; isc_netaddr_t na; dns_peer_t *peer = NULL; isc_buffer_t *tsigbuf = NULL; char *journalfile; char msg[NS_CLIENT_ACLMSGSIZE("zone transfer")]; char keyname[DNS_NAME_FORMATSIZE]; isc_boolean_t is_poll = ISC_FALSE; isc_boolean_t is_dlz = ISC_FALSE; isc_boolean_t is_ixfr = ISC_FALSE; isc_uint32_t begin_serial = 0, current_serial; switch (reqtype) { case dns_rdatatype_axfr: mnemonic = "AXFR"; break; case dns_rdatatype_ixfr: mnemonic = "IXFR"; break; default: INSIST(0); break; } ns_client_log(client, DNS_LOGCATEGORY_XFER_OUT, NS_LOGMODULE_XFER_OUT, ISC_LOG_DEBUG(6), "%s request", mnemonic); /* * Apply quota. */ result = isc_quota_attach(&ns_g_server->xfroutquota, "a); if (result != ISC_R_SUCCESS) { isc_log_write(XFROUT_COMMON_LOGARGS, ISC_LOG_WARNING, "%s request denied: %s", mnemonic, isc_result_totext(result)); goto failure; } /* * Interpret the question section. */ result = dns_message_firstname(request, DNS_SECTION_QUESTION); INSIST(result == ISC_R_SUCCESS); /* * The question section must contain exactly one question, and * it must be for AXFR/IXFR as appropriate. */ question_name = NULL; dns_message_currentname(request, DNS_SECTION_QUESTION, &question_name); question_rdataset = ISC_LIST_HEAD(question_name->list); question_class = question_rdataset->rdclass; INSIST(question_rdataset->type == reqtype); if (ISC_LIST_NEXT(question_rdataset, link) != NULL) FAILC(DNS_R_FORMERR, "multiple questions"); result = dns_message_nextname(request, DNS_SECTION_QUESTION); if (result != ISC_R_NOMORE) FAILC(DNS_R_FORMERR, "multiple questions"); result = dns_zt_find(client->view->zonetable, question_name, 0, NULL, &zone); if (result != ISC_R_SUCCESS) { /* * Normal zone table does not have a match. * Try the DLZ database */ // Temporary: only searching the first DLZ database if (! ISC_LIST_EMPTY(client->view->dlz_searched)) { result = dns_dlzallowzonexfr(client->view, question_name, &client->peeraddr, &db); pfilter_notify(result, client, "zonexfr"); if (result == ISC_R_NOPERM) { char _buf1[DNS_NAME_FORMATSIZE]; char _buf2[DNS_RDATACLASS_FORMATSIZE]; result = DNS_R_REFUSED; dns_name_format(question_name, _buf1, sizeof(_buf1)); dns_rdataclass_format(question_class, _buf2, sizeof(_buf2)); ns_client_log(client, DNS_LOGCATEGORY_SECURITY, NS_LOGMODULE_XFER_OUT, ISC_LOG_ERROR, "zone transfer '%s/%s' denied", _buf1, _buf2); goto failure; } if (result != ISC_R_SUCCESS) FAILQ(DNS_R_NOTAUTH, "non-authoritative zone", question_name, question_class); is_dlz = ISC_TRUE; } else { /* * not DLZ and not in normal zone table, we are * not authoritative */ FAILQ(DNS_R_NOTAUTH, "non-authoritative zone", question_name, question_class); } } else { /* zone table has a match */ switch(dns_zone_gettype(zone)) { /* Master and slave zones are OK for transfer. */ case dns_zone_master: case dns_zone_slave: case dns_zone_dlz: break; default: FAILQ(DNS_R_NOTAUTH, "non-authoritative zone", question_name, question_class); } CHECK(dns_zone_getdb(zone, &db)); dns_db_currentversion(db, &ver); } xfrout_log1(client, question_name, question_class, ISC_LOG_DEBUG(6), "%s question section OK", mnemonic); /* * Check the authority section. Look for a SOA record with * the same name and class as the question. */ for (result = dns_message_firstname(request, DNS_SECTION_AUTHORITY); result == ISC_R_SUCCESS; result = dns_message_nextname(request, DNS_SECTION_AUTHORITY)) { soa_name = NULL; dns_message_currentname(request, DNS_SECTION_AUTHORITY, &soa_name); /* * Ignore data whose owner name is not the zone apex. */ if (! dns_name_equal(soa_name, question_name)) continue; for (soa_rdataset = ISC_LIST_HEAD(soa_name->list); soa_rdataset != NULL; soa_rdataset = ISC_LIST_NEXT(soa_rdataset, link)) { /* * Ignore non-SOA data. */ if (soa_rdataset->type != dns_rdatatype_soa) continue; if (soa_rdataset->rdclass != question_class) continue; CHECK(dns_rdataset_first(soa_rdataset)); dns_rdataset_current(soa_rdataset, &soa_rdata); result = dns_rdataset_next(soa_rdataset); if (result == ISC_R_SUCCESS) FAILC(DNS_R_FORMERR, "IXFR authority section " "has multiple SOAs"); have_soa = ISC_TRUE; goto got_soa; } } got_soa: if (result != ISC_R_NOMORE) CHECK(result); xfrout_log1(client, question_name, question_class, ISC_LOG_DEBUG(6), "%s authority section OK", mnemonic); /* * If not a DLZ zone, decide whether to allow this transfer. */ if (!is_dlz) { ns_client_aclmsg("zone transfer", question_name, reqtype, client->view->rdclass, msg, sizeof(msg)); CHECK(ns_client_checkacl(client, NULL, msg, dns_zone_getxfracl(zone), ISC_TRUE, ISC_LOG_ERROR)); } /* * AXFR over UDP is not possible. */ if (reqtype == dns_rdatatype_axfr && (client->attributes & NS_CLIENTATTR_TCP) == 0) FAILC(DNS_R_FORMERR, "attempted AXFR over UDP"); /* * Look up the requesting server in the peer table. */ isc_netaddr_fromsockaddr(&na, &client->peeraddr); (void)dns_peerlist_peerbyaddr(client->view->peers, &na, &peer); /* * Decide on the transfer format (one-answer or many-answers). */ if (peer != NULL) (void)dns_peer_gettransferformat(peer, &format); /* * Get a dynamically allocated copy of the current SOA. */ if (is_dlz) dns_db_currentversion(db, &ver); CHECK(dns_db_createsoatuple(db, ver, mctx, DNS_DIFFOP_EXISTS, ¤t_soa_tuple)); current_serial = dns_soa_getserial(¤t_soa_tuple->rdata); if (reqtype == dns_rdatatype_ixfr) { isc_boolean_t provide_ixfr; /* * Outgoing IXFR may have been disabled for this peer * or globally. */ provide_ixfr = client->view->provideixfr; if (peer != NULL) (void) dns_peer_getprovideixfr(peer, &provide_ixfr); if (provide_ixfr == ISC_FALSE) goto axfr_fallback; if (! have_soa) FAILC(DNS_R_FORMERR, "IXFR request missing SOA"); begin_serial = dns_soa_getserial(&soa_rdata); /* * RFC1995 says "If an IXFR query with the same or * newer version number than that of the server * is received, it is replied to with a single SOA * record of the server's current version, just as * in AXFR". The claim about AXFR is incorrect, * but other than that, we do as the RFC says. * * Sending a single SOA record is also how we refuse * IXFR over UDP (currently, we always do). */ if (DNS_SERIAL_GE(begin_serial, current_serial) || (client->attributes & NS_CLIENTATTR_TCP) == 0) { CHECK(soa_rrstream_create(mctx, db, ver, &stream)); is_poll = ISC_TRUE; goto have_stream; } journalfile = is_dlz ? NULL : dns_zone_getjournal(zone); if (journalfile != NULL) result = ixfr_rrstream_create(mctx, journalfile, begin_serial, current_serial, &data_stream); else result = ISC_R_NOTFOUND; if (result == ISC_R_NOTFOUND || result == ISC_R_RANGE) { xfrout_log1(client, question_name, question_class, ISC_LOG_DEBUG(4), "IXFR version not in journal, " "falling back to AXFR"); mnemonic = "AXFR-style IXFR"; goto axfr_fallback; } CHECK(result); is_ixfr = ISC_TRUE; } else { axfr_fallback: CHECK(axfr_rrstream_create(mctx, db, ver, &data_stream)); } /* * Bracket the data stream with SOAs. */ CHECK(soa_rrstream_create(mctx, db, ver, &soa_stream)); CHECK(compound_rrstream_create(mctx, &soa_stream, &data_stream, &stream)); soa_stream = NULL; data_stream = NULL; have_stream: CHECK(dns_message_getquerytsig(request, mctx, &tsigbuf)); /* * Create the xfrout context object. This transfers the ownership * of "stream", "db", "ver", and "quota" to the xfrout context object. */ if (is_dlz) CHECK(xfrout_ctx_create(mctx, client, request->id, question_name, reqtype, question_class, zone, db, ver, quota, stream, dns_message_gettsigkey(request), tsigbuf, 3600, 3600, (format == dns_many_answers) ? ISC_TRUE : ISC_FALSE, &xfr)); else CHECK(xfrout_ctx_create(mctx, client, request->id, question_name, reqtype, question_class, zone, db, ver, quota, stream, dns_message_gettsigkey(request), tsigbuf, dns_zone_getmaxxfrout(zone), dns_zone_getidleout(zone), (format == dns_many_answers) ? ISC_TRUE : ISC_FALSE, &xfr)); xfr->mnemonic = mnemonic; stream = NULL; quota = NULL; CHECK(xfr->stream->methods->first(xfr->stream)); if (xfr->tsigkey != NULL) dns_name_format(&xfr->tsigkey->name, keyname, sizeof(keyname)); else keyname[0] = '\0'; if (is_poll) xfrout_log1(client, question_name, question_class, ISC_LOG_DEBUG(1), "IXFR poll up to date%s%s", (xfr->tsigkey != NULL) ? ": TSIG " : "", keyname); else if (is_ixfr) xfrout_log1(client, question_name, question_class, ISC_LOG_INFO, "%s started%s%s (serial %u -> %u)", mnemonic, (xfr->tsigkey != NULL) ? ": TSIG " : "", keyname, begin_serial, current_serial); else xfrout_log1(client, question_name, question_class, ISC_LOG_INFO, "%s started%s%s (serial %u)", mnemonic, (xfr->tsigkey != NULL) ? ": TSIG " : "", keyname, current_serial); if (zone != NULL) { dns_zone_getraw(zone, &raw); mayberaw = (raw != NULL) ? raw : zone; if ((client->attributes & NS_CLIENTATTR_WANTEXPIRE) != 0 && dns_zone_gettype(mayberaw) == dns_zone_slave) { isc_time_t expiretime; isc_uint32_t secs; dns_zone_getexpiretime(zone, &expiretime); secs = isc_time_seconds(&expiretime); if (secs >= client->now && result == ISC_R_SUCCESS) { client->attributes |= NS_CLIENTATTR_HAVEEXPIRE; client->expire = secs - client->now; } } if (raw != NULL) dns_zone_detach(&raw); } /* * Hand the context over to sendstream(). Set xfr to NULL; * sendstream() is responsible for either passing the * context on to a later event handler or destroying it. */ sendstream(xfr); xfr = NULL; result = ISC_R_SUCCESS; failure: if (result == DNS_R_REFUSED) inc_stats(zone, dns_nsstatscounter_xfrrej); if (quota != NULL) isc_quota_detach("a); if (current_soa_tuple != NULL) dns_difftuple_free(¤t_soa_tuple); if (stream != NULL) stream->methods->destroy(&stream); if (soa_stream != NULL) soa_stream->methods->destroy(&soa_stream); if (data_stream != NULL) data_stream->methods->destroy(&data_stream); if (ver != NULL) dns_db_closeversion(db, &ver, ISC_FALSE); if (db != NULL) dns_db_detach(&db); if (zone != NULL) dns_zone_detach(&zone); /* XXX kludge */ if (xfr != NULL) { xfrout_fail(xfr, result, "setting up zone transfer"); } else if (result != ISC_R_SUCCESS) { ns_client_log(client, DNS_LOGCATEGORY_XFER_OUT, NS_LOGMODULE_XFER_OUT, ISC_LOG_DEBUG(3), "zone transfer setup failed"); ns_client_error(client, result); } }
/** * Main F&U routine. Called during the processing of every packet. * * Decides if "skb" should be processed, updating binding and session information. * * @param[in] skb packet being translated. * @param[in] tuple skb's summary. * @return indicator of what should happen to skb. */ verdict filtering_and_updating(struct packet *pkt, struct tuple *in_tuple) { struct ipv6hdr *hdr_ip6; verdict result = VERDICT_CONTINUE; log_debug("Step 2: Filtering and Updating"); switch (pkt_l3_proto(pkt)) { case L3PROTO_IPV6: /* ICMP errors should not be filtered or affect the tables. */ if (pkt_is_icmp6_error(pkt)) { log_debug("Packet is ICMPv6 error; skipping step..."); return VERDICT_CONTINUE; } /* Get rid of hairpinning loops and unwanted packets. */ hdr_ip6 = pkt_ip6_hdr(pkt); if (pool6_contains(&hdr_ip6->saddr)) { log_debug("Hairpinning loop. Dropping..."); inc_stats(pkt, IPSTATS_MIB_INADDRERRORS); return VERDICT_DROP; } if (!pool6_contains(&hdr_ip6->daddr)) { log_debug("Packet was rejected by pool6; dropping..."); inc_stats(pkt, IPSTATS_MIB_INADDRERRORS); return VERDICT_DROP; } break; case L3PROTO_IPV4: /* ICMP errors should not be filtered or affect the tables. */ if (pkt_is_icmp4_error(pkt)) { log_debug("Packet is ICMPv4 error; skipping step..."); return VERDICT_CONTINUE; } /* Get rid of unexpected packets */ if (!pool4_contains(pkt_ip4_hdr(pkt)->daddr)) { log_debug("Packet was rejected by pool4; dropping..."); inc_stats(pkt, IPSTATS_MIB_INADDRERRORS); return VERDICT_DROP; } break; } /* Process packet, according to its protocol. */ switch (pkt_l4_proto(pkt)) { case L4PROTO_UDP: switch (pkt_l3_proto(pkt)) { case L3PROTO_IPV6: result = ipv6_simple(pkt, in_tuple); break; case L3PROTO_IPV4: result = ipv4_simple(pkt, in_tuple); break; } break; case L4PROTO_TCP: result = tcp(pkt, in_tuple); break; case L4PROTO_ICMP: switch (pkt_l3_proto(pkt)) { case L3PROTO_IPV6: if (config_get_filter_icmpv6_info()) { log_debug("Packet is ICMPv6 info (ping); dropping due to policy."); inc_stats(pkt, IPSTATS_MIB_INDISCARDS); return VERDICT_DROP; } result = ipv6_simple(pkt, in_tuple); break; case L3PROTO_IPV4: result = ipv4_simple(pkt, in_tuple); break; } break; case L4PROTO_OTHER: WARN(true, "Unknown layer 4 protocol (%d)...", pkt_l4_proto(pkt)); break; } log_debug("Done: Step 2."); return result; }
/** * Extracts relevant data from "skb" and stores it in the "tuple" tuple. * * @param skb packet the data will be extracted from. * @param tuple this function will populate this value using "skb"'s contents. * @return whether packet processing should continue. */ verdict determine_in_tuple(struct sk_buff *skb, struct tuple *in_tuple) { struct icmphdr *icmp4; struct icmp6hdr *icmp6; verdict result = VER_CONTINUE; log_debug("Step 1: Determining the Incoming Tuple"); switch (skb_l3_proto(skb)) { case L3PROTO_IPV4: switch (skb_l4_proto(skb)) { case L4PROTO_UDP: result = ipv4_udp(skb, in_tuple); break; case L4PROTO_TCP: result = ipv4_tcp(skb, in_tuple); break; case L4PROTO_ICMP: icmp4 = icmp_hdr(skb); if (is_icmp4_info(icmp4->type)) { result = ipv4_icmp_info(skb, in_tuple); } else if (is_icmp4_error(icmp4->type)) { result = ipv4_icmp_err(skb, in_tuple); } else { log_debug("Unknown ICMPv4 type: %u. Dropping packet...", icmp4->type); inc_stats(skb, IPSTATS_MIB_INHDRERRORS); result = VER_DROP; } break; } break; case L3PROTO_IPV6: switch (skb_l4_proto(skb)) { case L4PROTO_UDP: result = ipv6_udp(skb, in_tuple); break; case L4PROTO_TCP: result = ipv6_tcp(skb, in_tuple); break; case L4PROTO_ICMP: icmp6 = icmp6_hdr(skb); if (is_icmp6_info(icmp6->icmp6_type)) { result = ipv6_icmp_info(skb, in_tuple); } else if (is_icmp6_error(icmp6->icmp6_type)) { result = ipv6_icmp_err(skb, in_tuple); } else { log_debug("Unknown ICMPv6 type: %u. Dropping packet...", icmp6->icmp6_type); inc_stats(skb, IPSTATS_MIB_INHDRERRORS); result = VER_DROP; } break; } break; } /* * We moved the transport-protocol-not-recognized ICMP errors to packet.c because they're * covered in validations. */ log_tuple(in_tuple); log_debug("Done step 1."); return result; }
verdict compute_out_tuple(struct tuple *in, struct tuple *out, struct packet *pkt_in) { struct session_entry *session; int error; log_debug("Step 3: Computing the Outgoing Tuple"); error = sessiondb_get(in, &session); if (error) { /* * Bogus ICMP errors might cause this because Filtering never cares for them, * so it's not critical. */ log_debug("Error code %d while trying to find the packet's session entry.", error); inc_stats(pkt_in, IPSTATS_MIB_INNOROUTES); return VERDICT_DROP; } /* * Though the end result is the same, the following section of code collides with the RFC * in a superfluous sense. * * If the IPv6 pool has multiple prefixes, algorithmically generating addresses at this point * is pointless because, in order to do that, we'd need to know which prefix was used when the * session was created. This bit of information would have to be extracted from the session. * However, the address already algorithmically generated also belongs to the session. * So why bother generating it again? Just copy it. * * Additionally, the RFC wants some information extracted from the BIB entry. * We *also* extract that information from the session because it's the same, by definition. * * And finally, the RFC wants some information extracted from the tuple. * Same deal. If you draw all the scenarios (weirdass ICMP errors included), it's always the * same as the session. * * Given all of that, I really don't understand why the RFC bothers with any of this, including * making a distinction between 3-tuples and 5-tuples. The outgoing tuple is always a copy of * the other side of the session, plain and simple. When you think about it, that last claim * makes sense even in a general sense. */ switch (in->l3_proto) { case L3PROTO_IPV6: out->l3_proto = L3PROTO_IPV4; out->l4_proto = in->l4_proto; out->src.addr4 = session->local4; out->dst.addr4 = session->remote4; break; case L3PROTO_IPV4: out->l3_proto = L3PROTO_IPV6; out->l4_proto = in->l4_proto; out->src.addr6 = session->local6; out->dst.addr6 = session->remote6; break; } session_return(session); log_tuple(out); log_debug("Done step 3."); return VERDICT_CONTINUE; }
/** * Main F&U routine. Called during the processing of every packet. * * Decides if "skb" should be processed, updating binding and session information. * * @param[in] skb packet being translated. * @param[in] tuple skb's summary. * @return indicator of what should happen to skb. */ verdict filtering_and_updating(struct sk_buff* skb, struct tuple *in_tuple) { struct ipv6hdr *hdr_ip6; verdict result = VER_CONTINUE; log_debug("Step 2: Filtering and Updating"); switch (skb_l3_proto(skb)) { case L3PROTO_IPV6: /* ICMP errors should not be filtered or affect the tables. */ if (skb_l4_proto(skb) == L4PROTO_ICMP && is_icmp6_error(icmp6_hdr(skb)->icmp6_type)) { log_debug("Packet is ICMPv6 error; skipping step..."); return VER_CONTINUE; } /* Get rid of hairpinning loops and unwanted packets. */ hdr_ip6 = ipv6_hdr(skb); if (pool6_contains(&hdr_ip6->saddr)) { log_debug("Hairpinning loop. Dropping..."); inc_stats(skb, IPSTATS_MIB_INADDRERRORS); return VER_DROP; } if (!pool6_contains(&hdr_ip6->daddr)) { log_debug("Packet was rejected by pool6; dropping..."); inc_stats(skb, IPSTATS_MIB_INADDRERRORS); return VER_DROP; } break; case L3PROTO_IPV4: /* ICMP errors should not be filtered or affect the tables. */ if (skb_l4_proto(skb) == L4PROTO_ICMP && is_icmp4_error(icmp_hdr(skb)->type)) { log_debug("Packet is ICMPv4 error; skipping step..."); return VER_CONTINUE; } /* Get rid of unexpected packets */ if (!pool4_contains(ip_hdr(skb)->daddr)) { log_debug("Packet was rejected by pool4; dropping..."); inc_stats(skb, IPSTATS_MIB_INADDRERRORS); return VER_DROP; } break; } /* Process packet, according to its protocol. */ switch (skb_l4_proto(skb)) { case L4PROTO_UDP: switch (skb_l3_proto(skb)) { case L3PROTO_IPV6: result = ipv6_simple(skb, in_tuple); break; case L3PROTO_IPV4: result = ipv4_simple(skb, in_tuple); break; } break; case L4PROTO_TCP: result = tcp(skb, in_tuple); break; case L4PROTO_ICMP: switch (skb_l3_proto(skb)) { case L3PROTO_IPV6: if (filter_icmpv6_info()) { log_debug("Packet is ICMPv6 info (ping); dropping due to policy."); inc_stats(skb, IPSTATS_MIB_INDISCARDS); return VER_DROP; } result = ipv6_simple(skb, in_tuple); break; case L3PROTO_IPV4: result = ipv4_simple(skb, in_tuple); break; } break; } log_debug("Done: Step 2."); return result; }