diff options
-rw-r--r-- | src/libcharon/encoding/payloads/id_payload.c | 68 | ||||
-rw-r--r-- | src/libcharon/encoding/payloads/id_payload.h | 6 | ||||
-rw-r--r-- | src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c | 40 | ||||
-rw-r--r-- | src/libcharon/plugins/stroke/stroke_config.c | 5 | ||||
-rw-r--r-- | src/libcharon/plugins/unity/unity_narrow.c | 2 | ||||
-rw-r--r-- | src/libcharon/plugins/vici/vici_config.c | 9 | ||||
-rw-r--r-- | src/libcharon/sa/ikev1/tasks/quick_mode.c | 16 | ||||
-rw-r--r-- | src/libstrongswan/selectors/traffic_selector.c | 33 | ||||
-rw-r--r-- | src/libstrongswan/selectors/traffic_selector.h | 31 |
9 files changed, 171 insertions, 39 deletions
diff --git a/src/libcharon/encoding/payloads/id_payload.c b/src/libcharon/encoding/payloads/id_payload.c index ae0b19a9d..43f28fc20 100644 --- a/src/libcharon/encoding/payloads/id_payload.c +++ b/src/libcharon/encoding/payloads/id_payload.c @@ -245,18 +245,20 @@ METHOD(id_payload_t, get_identification, identification_t*, * Create a traffic selector from an range ID */ static traffic_selector_t *get_ts_from_range(private_id_payload_t *this, - ts_type_t type) + ts_type_t type, + uint16_t from_port, uint16_t to_port) { return traffic_selector_create_from_bytes(this->protocol_id, type, - chunk_create(this->id_data.ptr, this->id_data.len / 2), this->port, - chunk_skip(this->id_data, this->id_data.len / 2), this->port ?: 65535); + chunk_create(this->id_data.ptr, this->id_data.len / 2), from_port, + chunk_skip(this->id_data, this->id_data.len / 2), to_port); } /** * Create a traffic selector from an subnet ID */ static traffic_selector_t *get_ts_from_subnet(private_id_payload_t *this, - ts_type_t type) + ts_type_t type, + uint16_t from_port, uint16_t to_port) { traffic_selector_t *ts; chunk_t net, netmask; @@ -269,7 +271,7 @@ static traffic_selector_t *get_ts_from_subnet(private_id_payload_t *this, netmask.ptr[i] = (netmask.ptr[i] ^ 0xFF) | net.ptr[i]; } ts = traffic_selector_create_from_bytes(this->protocol_id, type, - net, this->port, netmask, this->port ?: 65535); + net, from_port, netmask, to_port); chunk_free(&netmask); return ts; } @@ -278,51 +280,76 @@ static traffic_selector_t *get_ts_from_subnet(private_id_payload_t *this, * Create a traffic selector from an IP ID */ static traffic_selector_t *get_ts_from_ip(private_id_payload_t *this, - ts_type_t type) + ts_type_t type, + uint16_t from_port, uint16_t to_port) { return traffic_selector_create_from_bytes(this->protocol_id, type, - this->id_data, this->port, this->id_data, this->port ?: 65535); + this->id_data, from_port, this->id_data, to_port); } METHOD(id_payload_t, get_ts, traffic_selector_t*, - private_id_payload_t *this) + private_id_payload_t *this, id_payload_t *other_, bool initiator) { + private_id_payload_t *other = (private_id_payload_t *) other_; + uint16_t from_port, to_port; + + if (other && this->protocol_id == IPPROTO_GRE && other->protocol_id == IPPROTO_GRE) + { + if (initiator) + { + from_port = this->port; + to_port = other->port; + } + else + { + from_port = other->port; + to_port = this->port; + } + if (from_port == 0 && to_port == 0) + to_port = 0xffff; + } + else + { + from_port = this->port; + to_port = this->port ?: 0xffff; + } + switch (this->id_type) { case ID_IPV4_ADDR_SUBNET: if (this->id_data.len == 8) { - return get_ts_from_subnet(this, TS_IPV4_ADDR_RANGE); + return get_ts_from_subnet(this, TS_IPV4_ADDR_RANGE, from_port, to_port); } break; case ID_IPV6_ADDR_SUBNET: if (this->id_data.len == 32) { - return get_ts_from_subnet(this, TS_IPV6_ADDR_RANGE); + return get_ts_from_subnet(this, TS_IPV6_ADDR_RANGE, from_port, to_port); } break; case ID_IPV4_ADDR_RANGE: if (this->id_data.len == 8) { - return get_ts_from_range(this, TS_IPV4_ADDR_RANGE); + return get_ts_from_range(this, TS_IPV4_ADDR_RANGE, from_port, to_port); } break; case ID_IPV6_ADDR_RANGE: if (this->id_data.len == 32) { - return get_ts_from_range(this, TS_IPV6_ADDR_RANGE); + return get_ts_from_range(this, TS_IPV6_ADDR_RANGE, from_port, to_port); } break; case ID_IPV4_ADDR: if (this->id_data.len == 4) { - return get_ts_from_ip(this, TS_IPV4_ADDR_RANGE); + return get_ts_from_ip(this, TS_IPV4_ADDR_RANGE, from_port, to_port); } break; case ID_IPV6_ADDR: if (this->id_data.len == 16) { - return get_ts_from_ip(this, TS_IPV6_ADDR_RANGE); + return get_ts_from_ip(this, TS_IPV6_ADDR_RANGE, from_port, to_port); } break; default: @@ -397,7 +424,7 @@ id_payload_t *id_payload_create_from_identification(payload_type_t type, /* * Described in header. */ -id_payload_t *id_payload_create_from_ts(traffic_selector_t *ts) +id_payload_t *id_payload_create_from_ts(traffic_selector_t *ts, bool initiator) { private_id_payload_t *this; uint8_t mask; @@ -460,8 +487,17 @@ id_payload_t *id_payload_create_from_ts(traffic_selector_t *ts) ts->get_from_address(ts), ts->get_to_address(ts)); net->destroy(net); } - this->port = ts->get_from_port(ts); this->protocol_id = ts->get_protocol(ts); + if (initiator || this->protocol_id != IPPROTO_GRE) + { + this->port = ts->get_from_port(ts); + } + else + { + this->port = ts->get_to_port(ts); + if (this->port == 0xffff && ts->get_from_port(ts) == 0) + this->port = 0; + } this->payload_length += this->id_data.len; return &this->public; diff --git a/src/libcharon/encoding/payloads/id_payload.h b/src/libcharon/encoding/payloads/id_payload.h index df1d07553..7558e91f2 100644 --- a/src/libcharon/encoding/payloads/id_payload.h +++ b/src/libcharon/encoding/payloads/id_payload.h @@ -48,11 +48,11 @@ struct id_payload_t { identification_t *(*get_identification) (id_payload_t *this); /** - * Creates a traffic selector form a ID_ADDR_SUBNET/RANGE identity. + * Creates a traffic selector form a ID_ADDR_SUBNET/RANGE identity pair. * * @return traffic selector, NULL on failure */ - traffic_selector_t* (*get_ts)(id_payload_t *this); + traffic_selector_t* (*get_ts)(id_payload_t *this, id_payload_t *other, bool initiator); /** * Get encoded payload without fixed payload header (used for IKEv1). @@ -91,6 +91,6 @@ id_payload_t *id_payload_create_from_identification(payload_type_t type, * @param ts traffic selector * @return PLV1_ID id_paylad_t object. */ -id_payload_t *id_payload_create_from_ts(traffic_selector_t *ts); +id_payload_t *id_payload_create_from_ts(traffic_selector_t *ts, bool initiator); #endif /** ID_PAYLOAD_H_ @}*/ diff --git a/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c b/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c index 4e79dfced..27ea824ee 100644 --- a/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c +++ b/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c @@ -824,7 +824,18 @@ static struct xfrm_selector ts2selector(traffic_selector_t *src, ts2subnet(src, &sel.saddr, &sel.prefixlen_s); ts2ports(dst, &sel.dport, &sel.dport_mask); ts2ports(src, &sel.sport, &sel.sport_mask); - if ((sel.proto == IPPROTO_ICMP || sel.proto == IPPROTO_ICMPV6) && + if (sel.proto == IPPROTO_GRE) + { + sel.sport = htons(src->get_from_port(src)); + sel.dport = htons(src->get_to_port(src)); + sel.sport_mask = ~0; + sel.dport_mask = ~0; + if (sel.sport == htons(0) && sel.dport == htons(0xffff)) + { + sel.sport = sel.dport = sel.sport_mask = sel.dport_mask = 0; + } + } + else if ((sel.proto == IPPROTO_ICMP || sel.proto == IPPROTO_ICMPV6) && (sel.dport || sel.sport)) { /* the kernel expects the ICMP type and code in the source and @@ -848,7 +859,7 @@ static traffic_selector_t* selector2ts(struct xfrm_selector *sel, bool src) { u_char *addr; uint8_t prefixlen; - uint16_t port = 0; + uint16_t from_port = 0, to_port = 65535; host_t *host = NULL; if (src) @@ -857,7 +868,7 @@ static traffic_selector_t* selector2ts(struct xfrm_selector *sel, bool src) prefixlen = sel->prefixlen_s; if (sel->sport_mask) { - port = ntohs(sel->sport); + from_port = to_port = ntohs(sel->sport); } } else @@ -866,14 +877,27 @@ static traffic_selector_t* selector2ts(struct xfrm_selector *sel, bool src) prefixlen = sel->prefixlen_d; if (sel->dport_mask) { - port = ntohs(sel->dport); + from_port = to_port = ntohs(sel->dport); + } + } + if (sel->proto == IPPROTO_GRE) + { + if (sel->sport_mask) + { + from_port = ntohs(sel->sport); + to_port = ntohs(sel->dport); + } + else + { + from_port = 0; + to_port = 0xffff; } } - if (sel->proto == IPPROTO_ICMP || sel->proto == IPPROTO_ICMPV6) + else if (sel->proto == IPPROTO_ICMP || sel->proto == IPPROTO_ICMPV6) { /* convert ICMP[v6] message type and code as supplied by the kernel in * source and destination ports (both in network order) */ - port = (sel->sport >> 8) | (sel->dport & 0xff00); - port = ntohs(port); + from_port = (sel->sport >> 8) | (sel->dport & 0xff00); + from_port = to_port = ntohs(from_port); } /* The Linux 2.6 kernel does not set the selector's family field, * so as a kludge we additionally test the prefix length. @@ -890,7 +914,7 @@ static traffic_selector_t* selector2ts(struct xfrm_selector *sel, bool src) if (host) { return traffic_selector_create_from_subnet(host, prefixlen, - sel->proto, port, port ?: 65535); + sel->proto, from_port, to_port); } return NULL; } diff --git a/src/libcharon/plugins/stroke/stroke_config.c b/src/libcharon/plugins/stroke/stroke_config.c index ac0129210..b2060e2eb 100644 --- a/src/libcharon/plugins/stroke/stroke_config.c +++ b/src/libcharon/plugins/stroke/stroke_config.c @@ -936,6 +936,11 @@ static bool parse_protoport(char *token, uint16_t *from_port, *from_port = 0xffff; *to_port = 0; } + else if (*port && *protocol == IPPROTO_GRE) + { + p = strtol(port, &endptr, 0); + traffic_selector_split_grekey(p, from_port, to_port); + } else if (*port) { svc = getservbyname(port, NULL); diff --git a/src/libcharon/plugins/unity/unity_narrow.c b/src/libcharon/plugins/unity/unity_narrow.c index 227d24be8..7749d8c2c 100644 --- a/src/libcharon/plugins/unity/unity_narrow.c +++ b/src/libcharon/plugins/unity/unity_narrow.c @@ -247,7 +247,7 @@ METHOD(listener_t, message, bool, if (!first) { id_payload = (id_payload_t*)payload; - tsr = id_payload->get_ts(id_payload); + tsr = id_payload->get_ts(id_payload, NULL, FALSE); break; } first = FALSE; diff --git a/src/libcharon/plugins/vici/vici_config.c b/src/libcharon/plugins/vici/vici_config.c index b3e835f59..710b57994 100644 --- a/src/libcharon/plugins/vici/vici_config.c +++ b/src/libcharon/plugins/vici/vici_config.c @@ -680,8 +680,13 @@ CALLBACK(parse_ts, bool, } else if (*port && !streq(port, "any")) { - svc = getservbyname(port, NULL); - if (svc) + if (proto == IPPROTO_GRE) + { + p = strtol(port, &end, 0); + if (*end) return FALSE; + traffic_selector_split_grekey(p, &from, &to); + } + else if ((svc = getservbyname(port, NULL)) != NULL) { from = to = ntohs(svc->s_port); } diff --git a/src/libcharon/sa/ikev1/tasks/quick_mode.c b/src/libcharon/sa/ikev1/tasks/quick_mode.c index 49b476ad8..315a29abe 100644 --- a/src/libcharon/sa/ikev1/tasks/quick_mode.c +++ b/src/libcharon/sa/ikev1/tasks/quick_mode.c @@ -567,9 +567,9 @@ static void add_ts(private_quick_mode_t *this, message_t *message) { id_payload_t *id_payload; - id_payload = id_payload_create_from_ts(this->tsi); + id_payload = id_payload_create_from_ts(this->tsi, TRUE); message->add_payload(message, &id_payload->payload_interface); - id_payload = id_payload_create_from_ts(this->tsr); + id_payload = id_payload_create_from_ts(this->tsr, FALSE); message->add_payload(message, &id_payload->payload_interface); } @@ -580,7 +580,7 @@ static bool get_ts(private_quick_mode_t *this, message_t *message) { traffic_selector_t *tsi = NULL, *tsr = NULL; enumerator_t *enumerator; - id_payload_t *id_payload; + id_payload_t *idi = NULL, *idr = NULL; payload_t *payload; host_t *hsi, *hsr; bool first = TRUE; @@ -590,20 +590,22 @@ static bool get_ts(private_quick_mode_t *this, message_t *message) { if (payload->get_type(payload) == PLV1_ID) { - id_payload = (id_payload_t*)payload; - if (first) { - tsi = id_payload->get_ts(id_payload); + idi = (id_payload_t*)payload; first = FALSE; } else { - tsr = id_payload->get_ts(id_payload); + idr = (id_payload_t*)payload; break; } } } + if (idi && idr) { + tsi = idi->get_ts(idi, idr, TRUE); + tsr = idr->get_ts(idr, idi, FALSE); + } enumerator->destroy(enumerator); /* create host2host selectors if ID payloads missing */ diff --git a/src/libstrongswan/selectors/traffic_selector.c b/src/libstrongswan/selectors/traffic_selector.c index 12f160224..00c78a91b 100644 --- a/src/libstrongswan/selectors/traffic_selector.c +++ b/src/libstrongswan/selectors/traffic_selector.c @@ -199,6 +199,14 @@ static int print_icmp(printf_hook_data_t *data, uint16_t port) } /** + * Print GRE key + */ +static int print_grekey(printf_hook_data_t *data, uint16_t from_port, uint16_t to_port) +{ + return print_in_hook(data, "%d", traffic_selector_grekey(from_port, to_port)); +} + +/** * Described in header. */ int traffic_selector_printf_hook(printf_hook_data_t *data, @@ -302,7 +310,11 @@ int traffic_selector_printf_hook(printf_hook_data_t *data, /* build port string */ if (has_ports) { - if (this->from_port == this->to_port) + if (this->protocol == IPPROTO_GRE) + { + written += print_grekey(data, this->from_port, this->to_port); + } + else if (this->from_port == this->to_port) { struct servent *serv; @@ -376,7 +388,24 @@ METHOD(traffic_selector_t, get_subset, traffic_selector_t*, /* select protocol, which is not zero */ protocol = max(this->protocol, other->protocol); - if ((is_opaque(this) && is_opaque(other)) || + if (this->protocol == IPPROTO_GRE) + { + if (is_any(this)) + { + from_port = other->from_port; + to_port = other->to_port; + } + else if (is_any(other) || + (this->from_port == other->from_port && + this->to_port == other->to_port)) + { + from_port = this->from_port; + to_port = this->to_port; + } + else + return NULL; + } + else if ((is_opaque(this) && is_opaque(other)) || (is_opaque(this) && is_any(other)) || (is_opaque(other) && is_any(this))) { diff --git a/src/libstrongswan/selectors/traffic_selector.h b/src/libstrongswan/selectors/traffic_selector.h index a9f78303c..5b08e708c 100644 --- a/src/libstrongswan/selectors/traffic_selector.h +++ b/src/libstrongswan/selectors/traffic_selector.h @@ -120,6 +120,9 @@ struct traffic_selector_t { * 8 bits and the code in the least significant 8 bits. Use the utility * functions to extract them. * + * If the protocol is GRE, the high 16-bits of the 32-bit GRE key is stored + * in the from port. Use the utility function to merge and split them. + * * @return port */ uint16_t (*get_from_port)(traffic_selector_t *this); @@ -134,6 +137,9 @@ struct traffic_selector_t { * 8 bits and the code in the least significant 8 bits. Use the utility * functions to extract them. * + * If the protocol is GRE, the low 16-bits of the 32-bit GRE key is stored + * in the to port. Use the utility function to merge and split them. + * * @return port */ uint16_t (*get_to_port)(traffic_selector_t *this); @@ -278,6 +284,31 @@ int traffic_selector_cmp(traffic_selector_t *a, traffic_selector_t *b, void *opts); /** + * Reconstruct the 32-bit GRE KEY in host order from a from/to ports. + * + * @param from_port port number in host order + * @param to_port port number in host order + * @return GRE KEY in host order + */ +static inline uint32_t traffic_selector_grekey(uint16_t from_port, uint16_t to_port) +{ + return (from_port << 16) | to_port; +} + +/** + * Split 32-bit GRE KEY in host order to from/to ports. + * + * @param grekey grekey in host order + * @param from_port from port in host order + * @param to_port to port in host order + */ +static inline void traffic_selector_split_grekey(uint32_t grekey, uint16_t *from_port, uint16_t *to_port) +{ + *from_port = grekey >> 16; + *to_port = grekey & 0xffff; +} + +/** * Create a new traffic selector using human readable params. * * If protocol is ICMP or ICMPv6 the ports are interpreted as follows: If they |