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:57:48 +02:00
commit 3ea7165a27
8 changed files with 245 additions and 44 deletions

View File

@ -858,6 +858,14 @@ Instead of omitting either value
can be used to the same effect, e.g.
.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
.B %opaque
for RFC 4301 OPAQUE selectors, or a numerical range in the form

View File

@ -78,7 +78,8 @@
#
# PLUTO_MY_PORT
# 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
# is the IP address of our peer.
@ -97,7 +98,8 @@
#
# PLUTO_PEER_PORT
# 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
# is an optional user ID employed by the XAUTH protocol
@ -288,16 +290,41 @@ else
IPSEC_POLICY_OUT="$IPSEC_POLICY --dir out"
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?
if [ "$PLUTO_MY_PORT" != 0 ]
then
S_MY_PORT="--sport $PLUTO_MY_PORT"
D_MY_PORT="--dport $PLUTO_MY_PORT"
if [ -n "$ICMP_TYPE_OPTION" ]
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
if [ "$PLUTO_PEER_PORT" != 0 ]
then
S_PEER_PORT="--sport $PLUTO_PEER_PORT"
D_PEER_PORT="--dport $PLUTO_PEER_PORT"
if [ -n "$ICMP_TYPE_OPTION" ]
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
# resolve octal escape sequences

View File

@ -174,6 +174,27 @@ static char *make_vip_vars(private_updown_listener_t *this, ike_sa_t *ike_sa)
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,
private_updown_listener_t *this, ike_sa_t *ike_sa, child_sa_t *child_sa,
bool up)
@ -341,11 +362,11 @@ METHOD(listener_t, child_updown, bool,
ike_sa->get_unique_id(ike_sa),
me, ike_sa->get_my_id(ike_sa),
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),
other, ike_sa->get_other_id(ike_sa),
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),
xauth,
virtual_ip,

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2006-2012 Tobias Brunner
* Copyright (C) 2006-2013 Tobias Brunner
* Copyright (C) 2005-2009 Martin Willi
* Copyright (C) 2008 Andreas Steffen
* 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);
ts2ports(dst, &sel.dport, &sel.dport_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.user = 0;
@ -766,7 +777,7 @@ static traffic_selector_t* selector2ts(struct xfrm_selector *sel, bool src)
prefixlen = sel->prefixlen_s;
if (sel->sport_mask)
{
port = htons(sel->sport);
port = ntohs(sel->sport);
}
}
else
@ -775,10 +786,15 @@ static traffic_selector_t* selector2ts(struct xfrm_selector *sel, bool src)
prefixlen = sel->prefixlen_d;
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,
* so as a kludge we additionally test the prefix length.
*/

View File

@ -870,6 +870,28 @@ static int lookup_algorithm(transform_type_t type, int ikev2)
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.
* @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;
socklen_t *len = host->get_sockaddr_len(host);
u_int16_t port = htons(host->get_port(host));
memcpy(dest, addr, *len);
#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
dest_addr->sa_len = *len;
#endif
switch (dest_addr->sa_family)
if (!include_port)
{
case AF_INET:
{
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;
}
set_port(dest_addr, 0);
}
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);
ext->sadb_address_len = PFKEY_LEN(sizeof(*ext) + len);
size_t 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)
{
struct sadb_address *addr = (struct sadb_address*)PFKEY_EXT_ADD_NEXT(msg);
size_t len;
addr->sadb_address_exttype = type;
addr->sadb_address_proto = proto;
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);
}

View File

@ -70,6 +70,9 @@
* @defgroup jobs jobs
* @ingroup processing
*
* @defgroup selectors selectors
* @ingroup libstrongswan
*
* @defgroup threading threading
* @ingroup libstrongswan
*

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 Jan Hutter
* 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;
}
/**
* 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.
*/
@ -302,20 +318,35 @@ int traffic_selector_printf_hook(printf_hook_data_t *data,
{
struct servent *serv;
serv = getservbyport(htons(this->from_port), serv_proto);
if (serv)
if (this->protocol == IPPROTO_ICMP ||
this->protocol == IPPROTO_ICMPV6)
{
written += print_in_hook(data, "%s", serv->s_name);
written += print_icmp(data, this->from_port);
}
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))
{
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
{
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,
.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;
}

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 Jan Hutter
* Hochschule fuer Technik Rapperswil
@ -17,7 +17,7 @@
/**
* @defgroup traffic_selector traffic_selector
* @{ @ingroup config
* @{ @ingroup selectors
*/
#ifndef TRAFFIC_SELECTOR_H_
@ -62,7 +62,12 @@ extern enum_name_t *ts_type_name;
* Object representing a traffic selector entry.
*
* 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 {
@ -109,7 +114,11 @@ struct traffic_selector_t {
* Get starting port of this ts.
*
* 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
*/
@ -119,7 +128,11 @@ struct traffic_selector_t {
* Get ending port of this ts.
*
* 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
*/
@ -213,9 +226,36 @@ struct traffic_selector_t {
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.
*
* 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 type type of following addresses, such as TS_IPV4_ADDR_RANGE
* @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.
*
* 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 protocol protocol for this ts, such as TCP or UDP
* @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
* 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 type type of following addresses, such as TS_IPV4_ADDR_RANGE
* @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
* all protocols, all ports and the address range specified by the
* subnet.
* Additionally, a protocol and a port may be specified. Port ranges
* are not supported via this constructor.
* Additionally, a protocol and ports may be specified.
*
* 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 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,
* 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 from_port start of allowed port range