📄 kernel.c
字号:
* There may already be a %trap: replace it. * There may not be any broad eroute: add %hold. * Once the broad %hold is in place, delete the narrow one. */ if (rn != ro) { if (erouted(ro) ? !eroute_connection(sr, htonl(SPI_HOLD), SA_INT, SADB_X_SATYPE_INT , null_proto_info , ERO_REPLACE , "replace %trap with broad %hold") : !eroute_connection(sr, htonl(SPI_HOLD), SA_INT, SADB_X_SATYPE_INT , null_proto_info , ERO_ADD, "add broad %hold")) { return FALSE; } } if (!replace_bare_shunt(src, dst , BOTTOM_PRIO , SPI_HOLD , FALSE , transport_proto , "delete narrow %hold")) return FALSE; } sr->routing = rn; return TRUE;}/* install or remove eroute for SA Group */static boolsag_eroute(struct state *st, struct spd_route *sr , unsigned op, const char *opname){ unsigned int inner_proto, inner_satype; ipsec_spi_t inner_spi; struct pfkey_proto_info proto_info[4]; int i; bool tunnel; /* figure out the SPI and protocol (in two forms) * for the innermost transformation. */ i = sizeof(proto_info) / sizeof(proto_info[0]) - 1; proto_info[i].proto = 0; tunnel = FALSE; if (st->st_ah.present) { inner_spi = st->st_ah.attrs.spi; inner_proto = SA_AH; inner_satype = SADB_SATYPE_AH; i--; proto_info[i].proto = IPPROTO_AH; proto_info[i].encapsulation = st->st_ah.attrs.encapsulation; tunnel |= proto_info[i].encapsulation == ENCAPSULATION_MODE_TUNNEL; proto_info[i].reqid = sr->reqid; } if (st->st_esp.present) { inner_spi = st->st_esp.attrs.spi; inner_proto = SA_ESP; inner_satype = SADB_SATYPE_ESP; i--; proto_info[i].proto = IPPROTO_ESP; proto_info[i].encapsulation = st->st_esp.attrs.encapsulation; tunnel |= proto_info[i].encapsulation == ENCAPSULATION_MODE_TUNNEL; proto_info[i].reqid = sr->reqid + 1; } if (st->st_ipcomp.present) { inner_spi = st->st_ipcomp.attrs.spi; inner_proto = SA_COMP; inner_satype = SADB_X_SATYPE_COMP; i--; proto_info[i].proto = IPPROTO_COMP; proto_info[i].encapsulation = st->st_ipcomp.attrs.encapsulation; tunnel |= proto_info[i].encapsulation == ENCAPSULATION_MODE_TUNNEL; proto_info[i].reqid = sr->reqid + 2; } if (i == sizeof(proto_info) / sizeof(proto_info[0]) - 1) { impossible(); /* no transform at all! */ } if (tunnel) { int j; inner_spi = st->st_tunnel_out_spi; inner_proto = SA_IPIP; inner_satype = SADB_X_SATYPE_IPIP; proto_info[i].encapsulation = ENCAPSULATION_MODE_TUNNEL; for (j = i + 1; proto_info[j].proto; j++) { proto_info[j].encapsulation = ENCAPSULATION_MODE_TRANSPORT; } } return eroute_connection(sr , inner_spi, inner_proto, inner_satype, proto_info + i , op, opname);}/* compute a (host-order!) SPI to implement the policy in connection c */ipsec_spi_tshunt_policy_spi(struct connection *c, bool prospective){ /* note: these are in host order :-( */ static const ipsec_spi_t shunt_spi[] = { SPI_TRAP, /* --initiateontraffic */ SPI_PASS, /* --pass */ SPI_DROP, /* --drop */ SPI_REJECT, /* --reject */ }; static const ipsec_spi_t fail_spi[] = { 0, /* --none*/ SPI_PASS, /* --failpass */ SPI_DROP, /* --faildrop */ SPI_REJECT, /* --failreject */ }; return prospective ? shunt_spi[(c->policy & POLICY_SHUNT_MASK) >> POLICY_SHUNT_SHIFT] : fail_spi[(c->policy & POLICY_FAIL_MASK) >> POLICY_FAIL_SHIFT];}/* Add/replace/delete a shunt eroute. * Such an eroute determines the fate of packets without the use * of any SAs. These are defaults, in effect. * If a negotiation has not been attempted, use %trap. * If negotiation has failed, the choice between %trap/%pass/%drop/%reject * is specified in the policy of connection c. */static boolshunt_eroute(struct connection *c , struct spd_route *sr , enum routing_t rt_kind , unsigned int op, const char *opname){ /* We are constructing a special SAID for the eroute. * The destination doesn't seem to matter, but the family does. * The protocol is SA_INT -- mark this as shunt. * The satype has no meaning, but is required for PF_KEY header! * The SPI signifies the kind of shunt. */ ipsec_spi_t spi = shunt_policy_spi(c, rt_kind == RT_ROUTED_PROSPECTIVE); bool ok; if (spi == 0) { /* we're supposed to end up with no eroute: rejig op and opname */ switch (op) { case ERO_REPLACE: /* replace with nothing == delete */ op = ERO_DELETE; opname = "delete"; break; case ERO_ADD: /* add nothing == do nothing */ return TRUE; case ERO_DELETE: /* delete remains delete */ break; default: bad_case(op); } } if (sr->routing == RT_ROUTED_ECLIPSED && c->kind == CK_TEMPLATE) { /* We think that we have an eroute, but we don't. * Adjust the request and account for eclipses. */ passert(eclipsable(sr)); switch (op) { case ERO_REPLACE: /* really an add */ op = ERO_ADD; opname = "replace eclipsed"; eclipse_count--; break; case ERO_DELETE: /* delete unnecessary: we don't actually have an eroute */ eclipse_count--; return TRUE; case ERO_ADD: default: bad_case(op); } } else if (eclipse_count > 0 && op == ERO_DELETE && eclipsable(sr)) { /* maybe we are uneclipsing something */ struct spd_route *esr; struct connection *ue = eclipsed(c, &esr); if (ue != NULL) { esr->routing = RT_ROUTED_PROSPECTIVE; return shunt_eroute(ue, esr , RT_ROUTED_PROSPECTIVE, ERO_REPLACE, "restoring eclipsed"); } } ok = TRUE; if (kernel_ops->inbound_eroute) { ok = raw_eroute(&c->spd.that.host_addr, &c->spd.that.client , &c->spd.this.host_addr, &c->spd.this.client , htonl(spi) , SA_INT , 0 /* transport_proto is not relevant */ , SADB_X_SATYPE_INT, null_proto_info , 0 /* use lifetime */ , op | (SADB_X_SAFLAGS_INFLOW << ERO_FLAG_SHIFT) , opname); } return eroute_connection(sr, htonl(spi), SA_INT, SADB_X_SATYPE_INT , null_proto_info, op, opname) && ok;}/* * This is only called when s is a likely SAID with trailing protocol i.e. * it has the form :- * * %<keyword>:p * <ip-proto><spi>@a.b.c.d:p * * The task here is to remove the ":p" part so that the rest can be read * by another routine. */static const char *read_proto(const char * s, size_t * len, int * transport_proto){ const char * p; const char * ugh; unsigned long proto; size_t l; l = *len; p = memchr(s, ':', l); if (p == 0) { *transport_proto = 0; return 0; } ugh = ttoul(p+1, l-((p-s)+1), 10, &proto); if (ugh != 0) return ugh; if (proto > 65535) return "protocol number is too large, legal range is 0-65535"; *len = p-s; *transport_proto = proto; return 0;}/* scan /proc/net/ipsec_eroute every once in a while, looking for: * * - %hold shunts of which Pluto isn't aware. This situation could * be caused by lost ACQUIRE messages. When found, they will * added to orphan_holds. This in turn will lead to Opportunistic * initiation. * * - other kinds of shunts that haven't been used recently. These will be * deleted. They represent OE failures. * * - recording recent uses of tunnel eroutes so that rekeying decisions * can be made for OE connections. * * Here are some sample lines: * 10 10.3.2.1.0/24 -> 0.0.0.0/0 => %trap * 259 10.3.2.1.115/32 -> 10.19.75.161/32 => tun0x1002@10.19.75.145 * 71 10.44.73.97/32 -> 0.0.0.0/0 => %trap * 4119 10.44.73.97/32 -> 10.114.121.41/32 => %pass * Newer versions of KLIPS start each line with a 32-bit packet count. * If available, the count is used to detect whether a %pass shunt is in use. * * NOTE: execution time is quadratic in the number of eroutes since the * searching for each is sequential. If this becomes a problem, faster * searches could be implemented (hash or radix tree, for example). */voidscan_proc_shunts(void){ static const char procname[] = "/proc/net/ipsec_eroute"; FILE *f; time_t nw = now(); int lino; struct eroute_info *expired = NULL; event_schedule(EVENT_SHUNT_SCAN, SHUNT_SCAN_INTERVAL, NULL); DBG(DBG_CONTROL, DBG_log("scanning for shunt eroutes") ) /* free any leftover entries: they will be refreshed if still current */ while (orphaned_holds != NULL) { struct eroute_info *p = orphaned_holds; orphaned_holds = p->next; pfree(orphaned_holds); } /* decode the /proc file. Don't do anything strenuous to it * (certainly no PF_KEY stuff) to minimize the chance that it * might change underfoot. */ f = fopen(procname, "r"); if (f == NULL) return; /* for each line... */ for (lino = 1; ; lino++) { unsigned char buf[1024]; /* should be big enough */ chunk_t field[10]; /* 10 is loose upper bound */ chunk_t *ff; /* fixed fields (excluding optional count) */ int fi; struct eroute_info eri; char *cp; err_t context = "" , ugh = NULL; cp = fgets(buf, sizeof(buf), f); if (cp == NULL) break; /* break out each field * Note: if there are too many fields, just stop; * it will be diagnosed a little later. */ for (fi = 0; fi < (int)elemsof(field); fi++) { static const char sep[] = " \t\n"; /* field-separating whitespace */ size_t w; cp += strspn(cp, sep); /* find start of field */ w = strcspn(cp, sep); /* find width of field */ setchunk(field[fi], cp, w); cp += w; if (w == 0) break; } /* This odd do-hickey is to share error reporting code. * A break will get to that common code. The setting * of "ugh" and "context" parameterize it. */ do { /* Old entries have no packet count; new ones do. * check if things are as they should be. */ if (fi == 5) ff = &field[0]; /* old form, with no count */ else if (fi == 6) ff = &field[1]; /* new form, with count */ else { ugh = "has wrong number of fields"; break; } if (ff[1].len != 2 || strncmp(ff[1].ptr, "->", 2) != 0 || ff[3].len != 2 || strncmp(ff[3].ptr, "=>", 2) != 0) { ugh = "is missing -> or =>"; break; } /* actually digest fields of interest */ /* packet count */ eri.count = 0; if (ff != field) { context = "count field is malformed: "; ugh = ttoul(field[0].ptr, field[0].len, 10, &eri.count); if (ugh != NULL) break; } /* our client */ context = "source subnet field malformed: "; ugh = ttosubnet(ff[0].ptr, ff[0].len, AF_INET, &eri.ours);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -