Merge branch 'icmp'

Improves handling of ICMP[v6] traffic selectors that specify message type and
code.

Fixes #421.
This commit is contained in:
Tobias Brunner
2013-10-17 16:59:07 +02:00
8 changed files with 245 additions and 44 deletions
+8
View File
@@ -858,6 +858,14 @@ Instead of omitting either value
can be used to the same effect, e.g. can be used to the same effect, e.g.
.BR leftsubnet=fec1::1[udp/%any],10.0.0.0/16[%any/53] . .BR leftsubnet=fec1::1[udp/%any],10.0.0.0/16[%any/53] .
If the protocol is
.B icmp
or
.B ipv6-icmp
the port is interpreted as ICMP message type if it is less than 256 or as type
and code if it is greater or equal to 256, with the type in the most significant
8 bits and the code in the least significant 8 bits.
The port value can alternatively take the value The port value can alternatively take the value
.B %opaque .B %opaque
for RFC 4301 OPAQUE selectors, or a numerical range in the form for RFC 4301 OPAQUE selectors, or a numerical range in the form
+33 -6
View File
@@ -78,7 +78,8 @@
# #
# PLUTO_MY_PORT # PLUTO_MY_PORT
# is the UDP/TCP port to which the IPsec SA is # is the UDP/TCP port to which the IPsec SA is
# restricted on our side. # restricted on our side. For ICMP/ICMPv6 this contains the
# message type, and PLUTO_PEER_PORT the message code.
# #
# PLUTO_PEER # PLUTO_PEER
# is the IP address of our peer. # is the IP address of our peer.
@@ -97,7 +98,8 @@
# #
# PLUTO_PEER_PORT # PLUTO_PEER_PORT
# is the UDP/TCP port to which the IPsec SA is # is the UDP/TCP port to which the IPsec SA is
# restricted on the peer side. # restricted on the peer side. For ICMP/ICMPv6 this contains the
# message code, and PLUTO_MY_PORT the message type.
# #
# PLUTO_XAUTH_ID # PLUTO_XAUTH_ID
# is an optional user ID employed by the XAUTH protocol # is an optional user ID employed by the XAUTH protocol
@@ -288,16 +290,41 @@ else
IPSEC_POLICY_OUT="$IPSEC_POLICY --dir out" IPSEC_POLICY_OUT="$IPSEC_POLICY --dir out"
fi fi
# use protocol specific options to set ports
case "$PLUTO_MY_PROTOCOL" in
1) # ICMP
ICMP_TYPE_OPTION="--icmp-type"
;;
58) # ICMPv6
ICMP_TYPE_OPTION="--icmpv6-type"
;;
*)
;;
esac
# are there port numbers? # are there port numbers?
if [ "$PLUTO_MY_PORT" != 0 ] if [ "$PLUTO_MY_PORT" != 0 ]
then then
S_MY_PORT="--sport $PLUTO_MY_PORT" if [ -n "$ICMP_TYPE_OPTION" ]
D_MY_PORT="--dport $PLUTO_MY_PORT" then
S_MY_PORT="$ICMP_TYPE_OPTION $PLUTO_MY_PORT"
D_MY_PORT="$ICMP_TYPE_OPTION $PLUTO_MY_PORT"
else
S_MY_PORT="--sport $PLUTO_MY_PORT"
D_MY_PORT="--dport $PLUTO_MY_PORT"
fi
fi fi
if [ "$PLUTO_PEER_PORT" != 0 ] if [ "$PLUTO_PEER_PORT" != 0 ]
then then
S_PEER_PORT="--sport $PLUTO_PEER_PORT" if [ -n "$ICMP_TYPE_OPTION" ]
D_PEER_PORT="--dport $PLUTO_PEER_PORT" then
# the syntax is --icmp[v6]-type type[/code], so add it to the existing option
S_MY_PORT="$S_MY_PORT/$PLUTO_PEER_PORT"
D_MY_PORT="$D_MY_PORT/$PLUTO_PEER_PORT"
else
S_PEER_PORT="--sport $PLUTO_PEER_PORT"
D_PEER_PORT="--dport $PLUTO_PEER_PORT"
fi
fi fi
# resolve octal escape sequences # resolve octal escape sequences
+23 -2
View File
@@ -174,6 +174,27 @@ static char *make_vip_vars(private_updown_listener_t *this, ike_sa_t *ike_sa)
return strdup(total); return strdup(total);
} }
/**
* Determine proper values for port env variable
*/
static u_int16_t get_port(traffic_selector_t *me,
traffic_selector_t *other, bool local)
{
switch (max(me->get_protocol(me), other->get_protocol(other)))
{
case IPPROTO_ICMP:
case IPPROTO_ICMPV6:
{
u_int16_t port = me->get_from_port(me);
port = max(port, other->get_from_port(other));
return local ? traffic_selector_icmp_type(port)
: traffic_selector_icmp_code(port);
}
}
return local ? me->get_from_port(me) : other->get_from_port(other);
}
METHOD(listener_t, child_updown, bool, METHOD(listener_t, child_updown, bool,
private_updown_listener_t *this, ike_sa_t *ike_sa, child_sa_t *child_sa, private_updown_listener_t *this, ike_sa_t *ike_sa, child_sa_t *child_sa,
bool up) bool up)
@@ -341,11 +362,11 @@ METHOD(listener_t, child_updown, bool,
ike_sa->get_unique_id(ike_sa), ike_sa->get_unique_id(ike_sa),
me, ike_sa->get_my_id(ike_sa), me, ike_sa->get_my_id(ike_sa),
my_client, my_client_mask, my_client, my_client_mask,
my_ts->get_from_port(my_ts), get_port(my_ts, other_ts, TRUE),
my_ts->get_protocol(my_ts), my_ts->get_protocol(my_ts),
other, ike_sa->get_other_id(ike_sa), other, ike_sa->get_other_id(ike_sa),
other_client, other_client_mask, other_client, other_client_mask,
other_ts->get_from_port(other_ts), get_port(my_ts, other_ts, FALSE),
other_ts->get_protocol(other_ts), other_ts->get_protocol(other_ts),
xauth, xauth,
virtual_ip, virtual_ip,
@@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2006-2012 Tobias Brunner * Copyright (C) 2006-2013 Tobias Brunner
* Copyright (C) 2005-2009 Martin Willi * Copyright (C) 2005-2009 Martin Willi
* Copyright (C) 2008 Andreas Steffen * Copyright (C) 2008 Andreas Steffen
* Copyright (C) 2006-2007 Fabian Hartmann, Noah Heusser * Copyright (C) 2006-2007 Fabian Hartmann, Noah Heusser
@@ -744,6 +744,17 @@ static struct xfrm_selector ts2selector(traffic_selector_t *src,
ts2subnet(src, &sel.saddr, &sel.prefixlen_s); ts2subnet(src, &sel.saddr, &sel.prefixlen_s);
ts2ports(dst, &sel.dport, &sel.dport_mask); ts2ports(dst, &sel.dport, &sel.dport_mask);
ts2ports(src, &sel.sport, &sel.sport_mask); ts2ports(src, &sel.sport, &sel.sport_mask);
if ((sel.proto == IPPROTO_ICMP || sel.proto == IPPROTO_ICMPV6) &&
(sel.dport || sel.sport))
{
/* the ICMP type is encoded in the most significant 8 bits and the ICMP
* code in the least significant 8 bits of the port. via XFRM we have
* to pass the ICMP type and code in the source and destination port
* fields, respectively. the port is in network byte order. */
u_int16_t port = max(sel.dport, sel.sport);
sel.sport = htons(port & 0xff);
sel.dport = htons(port >> 8);
}
sel.ifindex = 0; sel.ifindex = 0;
sel.user = 0; sel.user = 0;
@@ -766,7 +777,7 @@ static traffic_selector_t* selector2ts(struct xfrm_selector *sel, bool src)
prefixlen = sel->prefixlen_s; prefixlen = sel->prefixlen_s;
if (sel->sport_mask) if (sel->sport_mask)
{ {
port = htons(sel->sport); port = ntohs(sel->sport);
} }
} }
else else
@@ -775,10 +786,15 @@ static traffic_selector_t* selector2ts(struct xfrm_selector *sel, bool src)
prefixlen = sel->prefixlen_d; prefixlen = sel->prefixlen_d;
if (sel->dport_mask) if (sel->dport_mask)
{ {
port = htons(sel->dport); port = ntohs(sel->dport);
} }
} }
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);
}
/* The Linux 2.6 kernel does not set the selector's family field, /* The Linux 2.6 kernel does not set the selector's family field,
* so as a kludge we additionally test the prefix length. * so as a kludge we additionally test the prefix length.
*/ */
@@ -870,6 +870,28 @@ static int lookup_algorithm(transform_type_t type, int ikev2)
return alg; return alg;
} }
/**
* Helper to set a port in a sockaddr_t, the port has to be in host order
*/
static void set_port(sockaddr_t *addr, u_int16_t port)
{
switch (addr->sa_family)
{
case AF_INET:
{
struct sockaddr_in *sin = (struct sockaddr_in*)addr;
sin->sin_port = htons(port);
break;
}
case AF_INET6:
{
struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)addr;
sin6->sin6_port = htons(port);
break;
}
}
}
/** /**
* Copy a host_t as sockaddr_t to the given memory location. * Copy a host_t as sockaddr_t to the given memory location.
* @return the number of bytes copied * @return the number of bytes copied
@@ -878,37 +900,38 @@ static size_t hostcpy(void *dest, host_t *host, bool include_port)
{ {
sockaddr_t *addr = host->get_sockaddr(host), *dest_addr = dest; sockaddr_t *addr = host->get_sockaddr(host), *dest_addr = dest;
socklen_t *len = host->get_sockaddr_len(host); socklen_t *len = host->get_sockaddr_len(host);
u_int16_t port = htons(host->get_port(host));
memcpy(dest, addr, *len); memcpy(dest, addr, *len);
#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
dest_addr->sa_len = *len; dest_addr->sa_len = *len;
#endif #endif
switch (dest_addr->sa_family) if (!include_port)
{ {
case AF_INET: set_port(dest_addr, 0);
{
struct sockaddr_in *sin = dest;
sin->sin_port = include_port ? port : 0;
break;
}
case AF_INET6:
{
struct sockaddr_in6 *sin6 = dest;
sin6->sin6_port = include_port ? port : 0;
break;
}
} }
return *len; return *len;
} }
/** /**
* add a host behind an sadb_address extension * Copy a host_t as sockaddr_t to the given memory location and map the port to
* ICMP/ICMPv6 message type/code as the Linux kernel expects it, that is, the
* type in the source and the code in the destination address.
* @return the number of bytes copied
*/ */
static void host2ext(host_t *host, struct sadb_address *ext, bool include_port) static size_t hostcpy_icmp(void *dest, host_t *host, u_int16_t type)
{ {
size_t len = hostcpy(ext + 1, host, include_port); size_t len;
ext->sadb_address_len = PFKEY_LEN(sizeof(*ext) + len);
len = hostcpy(dest, host, TRUE);
if (type == SADB_EXT_ADDRESS_SRC)
{
set_port(dest, traffic_selector_icmp_type(host->get_port(host)));
}
else
{
set_port(dest, traffic_selector_icmp_code(host->get_port(host)));
}
return len;
} }
/** /**
@@ -918,10 +941,20 @@ static void add_addr_ext(struct sadb_msg *msg, host_t *host, u_int16_t type,
u_int8_t proto, u_int8_t prefixlen, bool include_port) u_int8_t proto, u_int8_t prefixlen, bool include_port)
{ {
struct sadb_address *addr = (struct sadb_address*)PFKEY_EXT_ADD_NEXT(msg); struct sadb_address *addr = (struct sadb_address*)PFKEY_EXT_ADD_NEXT(msg);
size_t len;
addr->sadb_address_exttype = type; addr->sadb_address_exttype = type;
addr->sadb_address_proto = proto; addr->sadb_address_proto = proto;
addr->sadb_address_prefixlen = prefixlen; addr->sadb_address_prefixlen = prefixlen;
host2ext(host, addr, include_port); if (proto == IPPROTO_ICMP || proto == IPPROTO_ICMPV6)
{
len = hostcpy_icmp(addr + 1, host, type);
}
else
{
len = hostcpy(addr + 1, host, include_port);
}
addr->sadb_address_len = PFKEY_LEN(sizeof(*addr) + len);
PFKEY_EXT_ADD(msg, addr); PFKEY_EXT_ADD(msg, addr);
} }
+3
View File
@@ -70,6 +70,9 @@
* @defgroup jobs jobs * @defgroup jobs jobs
* @ingroup processing * @ingroup processing
* *
* @defgroup selectors selectors
* @ingroup libstrongswan
*
* @defgroup threading threading * @defgroup threading threading
* @ingroup libstrongswan * @ingroup libstrongswan
* *
+41 -6
View File
@@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2007-2009 Tobias Brunner * Copyright (C) 2007-2013 Tobias Brunner
* Copyright (C) 2005-2007 Martin Willi * Copyright (C) 2005-2007 Martin Willi
* Copyright (C) 2005 Jan Hutter * Copyright (C) 2005 Jan Hutter
* Hochschule fuer Technik Rapperswil * Hochschule fuer Technik Rapperswil
@@ -193,6 +193,22 @@ static bool is_any(private_traffic_selector_t *this)
return this->from_port == 0 && this->to_port == 0xffff; return this->from_port == 0 && this->to_port == 0xffff;
} }
/**
* Print ICMP/ICMPv6 type and code
*/
static int print_icmp(printf_hook_data_t *data, u_int16_t port)
{
u_int8_t type, code;
type = traffic_selector_icmp_type(port);
code = traffic_selector_icmp_code(port);
if (code)
{
return print_in_hook(data, "%d(%d)", type, code);
}
return print_in_hook(data, "%d", type);
}
/** /**
* Described in header. * Described in header.
*/ */
@@ -302,20 +318,35 @@ int traffic_selector_printf_hook(printf_hook_data_t *data,
{ {
struct servent *serv; struct servent *serv;
serv = getservbyport(htons(this->from_port), serv_proto); if (this->protocol == IPPROTO_ICMP ||
if (serv) this->protocol == IPPROTO_ICMPV6)
{ {
written += print_in_hook(data, "%s", serv->s_name); written += print_icmp(data, this->from_port);
} }
else else
{ {
written += print_in_hook(data, "%d", this->from_port); serv = getservbyport(htons(this->from_port), serv_proto);
if (serv)
{
written += print_in_hook(data, "%s", serv->s_name);
}
else
{
written += print_in_hook(data, "%d", this->from_port);
}
} }
} }
else if (is_opaque(this)) else if (is_opaque(this))
{ {
written += print_in_hook(data, "OPAQUE"); written += print_in_hook(data, "OPAQUE");
} }
else if (this->protocol == IPPROTO_ICMP ||
this->protocol == IPPROTO_ICMPV6)
{
written += print_icmp(data, this->from_port);
written += print_in_hook(data, "-");
written += print_icmp(data, this->to_port);
}
else else
{ {
written += print_in_hook(data, "%d-%d", written += print_in_hook(data, "%d-%d",
@@ -910,6 +941,10 @@ static private_traffic_selector_t *traffic_selector_create(u_int8_t protocol,
.protocol = protocol, .protocol = protocol,
.type = type, .type = type,
); );
if (protocol == IPPROTO_ICMP || protocol == IPPROTO_ICMPV6)
{
this->from_port = from_port < 256 ? from_port << 8 : from_port;
this->to_port = to_port < 256 ? to_port << 8 : to_port;
}
return this; return this;
} }
+65 -7
View File
@@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2007 Tobias Brunner * Copyright (C) 2007-2013 Tobias Brunner
* Copyright (C) 2005-2006 Martin Willi * Copyright (C) 2005-2006 Martin Willi
* Copyright (C) 2005 Jan Hutter * Copyright (C) 2005 Jan Hutter
* Hochschule fuer Technik Rapperswil * Hochschule fuer Technik Rapperswil
@@ -17,7 +17,7 @@
/** /**
* @defgroup traffic_selector traffic_selector * @defgroup traffic_selector traffic_selector
* @{ @ingroup config * @{ @ingroup selectors
*/ */
#ifndef TRAFFIC_SELECTOR_H_ #ifndef TRAFFIC_SELECTOR_H_
@@ -62,7 +62,12 @@ extern enum_name_t *ts_type_name;
* Object representing a traffic selector entry. * Object representing a traffic selector entry.
* *
* A traffic selector defines an range of addresses * A traffic selector defines an range of addresses
* and a range of ports. IPv6 is not fully supported yet. * and a range of ports.
*
* If the protocol is ICMP or ICMPv6 the ICMP type and code are stored in the
* port field as follows: The message type is placed in the most significant
* 8 bits and the code in the least significant 8 bits. Utility functions are
* provided to extract the individual values.
*/ */
struct traffic_selector_t { struct traffic_selector_t {
@@ -109,7 +114,11 @@ struct traffic_selector_t {
* Get starting port of this ts. * Get starting port of this ts.
* *
* Port is in host order, since the parser converts it. * Port is in host order, since the parser converts it.
* Size depends on protocol. *
* If the protocol is ICMP/ICMPv6 the ICMP type and code are stored in this
* field as follows: The message type is placed in the most significant
* 8 bits and the code in the least significant 8 bits. Use the utility
* functions to extract them.
* *
* @return port * @return port
*/ */
@@ -119,7 +128,11 @@ struct traffic_selector_t {
* Get ending port of this ts. * Get ending port of this ts.
* *
* Port is in host order, since the parser converts it. * Port is in host order, since the parser converts it.
* Size depends on protocol. *
* If the protocol is ICMP/ICMPv6 the ICMP type and code are stored in this
* field as follows: The message type is placed in the most significant
* 8 bits and the code in the least significant 8 bits. Use the utility
* functions to extract them.
* *
* @return port * @return port
*/ */
@@ -213,9 +226,36 @@ struct traffic_selector_t {
void (*destroy) (traffic_selector_t *this); void (*destroy) (traffic_selector_t *this);
}; };
/**
* Extract the ICMP/ICMPv6 message type from a port in host order
*
* @param port port number in host order
* @return ICMP/ICMPv6 message type
*/
static inline u_int8_t traffic_selector_icmp_type(u_int16_t port)
{
return port >> 8;
}
/**
* Extract the ICMP/ICMPv6 message code from a port in host order
*
* @param port port number in host order
* @return ICMP/ICMPv6 message code
*/
static inline u_int8_t traffic_selector_icmp_code(u_int16_t port)
{
return port & 0xff;
}
/** /**
* Create a new traffic selector using human readable params. * Create a new traffic selector using human readable params.
* *
* If protocol is ICMP or ICMPv6 the ports are interpreted as follows: If they
* are less than 256 the value is assumed to be a message type, if they are
* greater or equal to 256 they are assumed to be type and code as defined
* for traffic_selector_t.
*
* @param protocol protocol for this ts, such as TCP or UDP * @param protocol protocol for this ts, such as TCP or UDP
* @param type type of following addresses, such as TS_IPV4_ADDR_RANGE * @param type type of following addresses, such as TS_IPV4_ADDR_RANGE
* @param from_addr start of address range as string * @param from_addr start of address range as string
@@ -236,6 +276,11 @@ traffic_selector_t *traffic_selector_create_from_string(
/** /**
* Create a traffic selector from a CIDR string. * Create a traffic selector from a CIDR string.
* *
* If protocol is ICMP or ICMPv6 the ports are interpreted as follows: If they
* are less than 256 the value is assumed to be a message type, if they are
* greater or equal to 256 they are assumed to be type and code as defined
* for traffic_selector_t.
*
* @param string CIDR string, such as 10.1.0.0/16 * @param string CIDR string, such as 10.1.0.0/16
* @param protocol protocol for this ts, such as TCP or UDP * @param protocol protocol for this ts, such as TCP or UDP
* @param from_port start of allowed port range * @param from_port start of allowed port range
@@ -253,6 +298,11 @@ traffic_selector_t *traffic_selector_create_from_cidr(
* But the parser gives us this data in this format, so we * But the parser gives us this data in this format, so we
* don't have to convert twice. * don't have to convert twice.
* *
* If protocol is ICMP or ICMPv6 the ports are interpreted as follows: If they
* are less than 256 the value is assumed to be a message type, if they are
* greater or equal to 256 they are assumed to be type and code as defined
* for traffic_selector_t.
*
* @param protocol protocol for this ts, such as TCP or UDP * @param protocol protocol for this ts, such as TCP or UDP
* @param type type of following addresses, such as TS_IPV4_ADDR_RANGE * @param type type of following addresses, such as TS_IPV4_ADDR_RANGE
* @param from_address start of address range, network order * @param from_address start of address range, network order
@@ -284,8 +334,12 @@ traffic_selector_t *traffic_selector_create_from_rfc3779_format(ts_type_t type,
* is sufficient. This constructor creates a traffic selector for * is sufficient. This constructor creates a traffic selector for
* all protocols, all ports and the address range specified by the * all protocols, all ports and the address range specified by the
* subnet. * subnet.
* Additionally, a protocol and a port may be specified. Port ranges * Additionally, a protocol and ports may be specified.
* are not supported via this constructor. *
* If protocol is ICMP or ICMPv6 the ports are interpreted as follows: If they
* are less than 256 the value is assumed to be a message type, if they are
* greater or equal to 256 they are assumed to be type and code as defined
* for traffic_selector_t.
* *
* @param net subnet to use * @param net subnet to use
* @param netbits size of the subnet, as used in e.g. 192.168.0.0/24 notation * @param netbits size of the subnet, as used in e.g. 192.168.0.0/24 notation
@@ -307,6 +361,10 @@ traffic_selector_t *traffic_selector_create_from_subnet(
* created at runtime using the external/virtual IP. Using this constructor, * created at runtime using the external/virtual IP. Using this constructor,
* a call to set_address() sets this traffic selector to the supplied host. * a call to set_address() sets this traffic selector to the supplied host.
* *
* If protocol is ICMP or ICMPv6 the ports are interpreted as follows: If they
* are less than 256 the value is assumed to be a message type, if they are
* greater or equal to 256 they are assumed to be type and code as defined
* for traffic_selector_t.
* *
* @param protocol upper layer protocl to allow * @param protocol upper layer protocl to allow
* @param from_port start of allowed port range * @param from_port start of allowed port range