Merge lp:~christof-mroz/hipl/hipfw-esp-speedup into lp:hipl

Proposed by Christof Mroz
Status: Merged
Merged at revision: 5829
Proposed branch: lp:~christof-mroz/hipl/hipfw-esp-speedup
Merge into: lp:hipl
Prerequisite: lp:~christof-mroz/hipl/hipfw-timeout
Diff against target: 1091 lines (+720/-22)
14 files modified
Makefile.am (+1/-0)
firewall/conntrack.c (+366/-7)
firewall/conntrack.h (+3/-0)
firewall/firewall.c (+1/-0)
firewall/firewall.h (+1/-0)
firewall/firewall_defines.h (+4/-0)
firewall/helpers.c (+54/-9)
firewall/helpers.h (+5/-1)
firewall/main.c (+25/-2)
firewall/pisa.c (+5/-0)
test/check_firewall.c (+3/-1)
test/firewall/conntrack.c (+192/-2)
test/firewall/helpers.c (+59/-0)
test/firewall/test_suites.h (+1/-0)
To merge this branch: bzr merge lp:~christof-mroz/hipl/hipfw-esp-speedup
Reviewer Review Type Date Requested Status
René Hummen Approve
Stefan Götz (community) Approve
Diego Biurrun Pending
Review via email: mp+56206@code.launchpad.net

This proposal supersedes a proposal from 2011-03-28.

Description of the change

Addressed the review comments.

To post a comment you must log in.
Revision history for this message
Miika Komu (miika-iki) wrote : Posted in a previous version of this proposal

How much is the speed up?

Revision history for this message
Christof Mroz (christof-mroz) wrote : Posted in a previous version of this proposal

> How much is the speed up?

See the branch description for iperf results (using VMs so far):
https://code.launchpad.net/~christof-mroz/hipl/hipfw-esp-speedup

Revision history for this message
Diego Biurrun (diego-biurrun) wrote : Posted in a previous version of this proposal
Download full text (4.5 KiB)

 review needs-fixing

On Tue, Mar 22, 2011 at 04:37:59PM +0000, Christof Mroz wrote:
> Christof Mroz has proposed merging lp:~christof-mroz/hipl/hipfw-esp-speedup into lp:hipl with lp:~christof-mroz/hipl/hipfw-timeout as a prerequisite.
>
> --- firewall/conntrack.c 2011-03-22 16:37:54 +0000
> +++ firewall/conntrack.c 2011-03-22 16:37:54 +0000
> @@ -340,23 +346,154 @@
>
> + * Set up or remove iptables rules to bypass userspace processing of the
> + * SPI/destination pairs as specified by @a esp_tuple and @a dest.
> + * This can greatly improve firewall throughput.
> + *
> + * @param esp_tuple Determines the SPI.
> + * @param dest The corresponding destination address to bypass. May be
> + * a IPv6-mapped IPv4 address.
> + * @param insert Insert new rule if true, remove existing if false.
> + * @return 0 if rules were modified, non-zero otherwise.

The function returns -1, not non-zero - positive numbers are non-zero as
well, so I suggest being doubly clear here...

> + * @note This interferes, in one way or another, with userspace_ipsec,
> + * Relay, LSI, midauth, lightweight-update and esp_prot. Care was
> + * taken to not break these features though.

nit: relay

> +static int hip_fw_manage_esp_rule(const struct esp_tuple *const esp_tuple,
> + const struct in6_addr *const dest, bool insert)
> +{
> +
> + if (IN6_IS_ADDR_V4MAPPED(dest)) {
> + char daddr[INET_ADDRSTRLEN];
> + struct in_addr dest4;
> +
> + IPV6_TO_IPV4_MAP(dest, &dest4);
> + HIP_IFEL(!inet_ntop(AF_INET, &dest4, daddr, sizeof(daddr)), -1,
> + "inet_ntop: %s\n", strerror(errno));
> + } else {
> + char daddr[INET6_ADDRSTRLEN];
> + HIP_IFEL(!inet_ntop(AF_INET6, dest, daddr, sizeof(daddr)), -1,
> + "inet_ntop: %s\n", strerror(errno));
> +
> + }
> +
> +out_err:
> + return err == EXIT_SUCCESS ? 0 : -1;
> +}

I'd solve this without goto/HIP_IFEL and just return directly.

> +/**
> + * Set up or remove iptables rules to bypass userspace processing of all
> + * SPI/destination pairs associated with @a esp_tuple.
> + *
> + * @param esp_tuple Determines the SPI and all destination addresses.
> + * @param insert Insert rules if true, remove existing if false.
> + *
> + * @see hip_fw_manage_esp_rule

hip_fw_manage_esp_rule() ?

I'm not sure, just asking...

> +void hip_fw_manage_esp_tuple(const struct esp_tuple *const esp_tuple, const bool insert)

nit: break such long lines

> @@ -448,7 +588,7 @@
> * @param data the connection-related data to be inserted
> * @see remove_connection
> */
> -static void insert_new_connection(const struct hip_data *data)
> +static void insert_new_connection(const struct hip_data *data, struct hip_fw_context *ctx)

ditto

> @@ -1983,6 +2108,173 @@
>
> /**
> + * Parse one line of iptables -nvL formatted output, and extract
> + * packet count, SPI and destination IP if successful.
> + * This takes into account all kinds of rules that can are created by
> + * hip_fw_manage_esp_rule().

can are?

> + * @param spi Out: receives the SPI.
> + * @param spi Out: rece...

Read more...

review: Needs Fixing
Revision history for this message
Stefan Götz (stefan.goetz-deactivatedaccount) wrote : Posted in a previous version of this proposal
Download full text (15.3 KiB)

Hi Christof!

Thanks for
- splitting the original merge proposal into smaller chunks
- providing hard numbers to show that we really want theses changes

I don't have a test setup, so I couldn't test this feature. Code comments only.

> === modified file 'firewall/conntrack.c'
> --- firewall/conntrack.c 2011-03-22 16:37:54 +0000
> +++ firewall/conntrack.c 2011-03-22 16:37:54 +0000
> @@ -340,23 +346,154 @@
> }
>
> /**
> - * Insert an address into a list of addresses. If same address exists already,
> - * the update_id is replaced with the new value.
> - *
> - * @param addr_list the address list
> - * @param addr the address to be added
> - * @param upd_id update id
> - *
> - * @return the address list
> - */
> -static struct slist *update_esp_address(struct slist *addr_list,
> - const struct in6_addr *addr,
> - const uint32_t *upd_id)
> -{
> - struct esp_address *esp_addr = get_esp_address(addr_list, addr);
> + * Set up or remove iptables rules to bypass userspace processing of the
> + * SPI/destination pairs as specified by @a esp_tuple and @a dest.
> + * This can greatly improve firewall throughput.
> + *
> + * @param esp_tuple Determines the SPI.
> + * @param dest The corresponding destination address to bypass. May be
> + * a IPv6-mapped IPv4 address.
> + * @param insert Insert new rule if true, remove existing if false.
> + * @return 0 if rules were modified, non-zero otherwise.
> + *
> + * @note This feature may be turned off completely by the -u command line option.
> + * It is also automatically deactivated for connections that demand
> + * more advanced connection tracking.
> + * In these cases, -1 is returned even though there was not even an
> + * attempt to modify rules.
> + *
> + * @note This interferes, in one way or another, with userspace_ipsec,
> + * Relay, LSI, midauth, lightweight-update and esp_prot. Care was
> + * taken to not break these features though.
> + *
> + * @see update_esp_address()
> + * @see free_esp_tuple()
> + * @see ::esp_speedup
> + */
> +static int hip_fw_manage_esp_rule(const struct esp_tuple *const esp_tuple,
> + const struct in6_addr *const dest, bool insert)

[M] 'const bool insert' for const correctness

> +/**
> + * Insert a destination address into an esp_tuple. If same address exists already,
> + * the update_id is replaced with the new value instead.
> + *
> + * @param esp_tuple the esp tuple
> + * @param addr the address to be added
> + * @param upd_id update id
> + */
> +static void update_esp_address(struct esp_tuple *esp_tuple,
> + const struct in6_addr *addr,
> + const uint32_t *upd_id)

[M] const correctness

> @@ -367,7 +504,7 @@
> *esp_addr->update_id = *upd_id;
> }
> HIP_DEBUG("update_esp_address: found and updated\n");
> - return addr_list;
> + return;
> }
> esp_addr = malloc(sizeof(struct esp_address));
> memcpy(&esp_addr->dst_addr, addr, sizeof(struct in6_addr));

[H] not you...

review: Needs Fixing
Revision history for this message
Diego Biurrun (diego-biurrun) wrote : Posted in a previous version of this proposal

On Sun, Mar 27, 2011 at 09:54:13PM +0000, Stefan Götz wrote:
>
> > === modified file 'firewall/conntrack.c'
> > --- firewall/conntrack.c 2011-03-22 16:37:54 +0000
> > +++ firewall/conntrack.c 2011-03-22 16:37:54 +0000
> > @@ -340,23 +346,154 @@
> >
> > +static int hip_fw_manage_esp_rule(const struct esp_tuple *const esp_tuple,
> > + const struct in6_addr *const dest, bool insert)
>
> [M] 'const bool insert' for const correctness

What's the point of adding const to a non-pointer value passed
to a function?

> > @@ -448,7 +588,7 @@
> > * @param data the connection-related data to be inserted
>
> [M] the 'ctx' parameter is not documented

Christof, does your code pass 'make alltests'? This should likely
be caught by 'make doxygen', which is a part of 'make alltests'.

Apparently we should create some sort of checklist to run through
before submitting merge proposals...

Diego

Revision history for this message
Christof Mroz (christof-mroz) wrote : Posted in a previous version of this proposal
Download full text (5.5 KiB)

On Sun, 27 Mar 2011 21:54:13 +0000
Stefan Götz <email address hidden> wrote:

> > + * @return true if rule was valid and output was
> > written,
> > + * false otherwise
>
> [L] Again, when is a rule 'valid'?

Explaining the input format in detail would be reciting the
implementation, I think. It already states that rules set up by
hip_fw_manage_esp_rule() are covered, which is the important info.

> > + * @param now We consider this the current time.
> > + * @return Number of rules that were identified with an
> > esp tuple.
> > + * Not necessarily the number of tuples updated.
> > + *
> > + * @note This function doesn't clear the packet counters itself.
>
> [M] Reading and zeroing the packet counters should be done atomically
> with a single command line invocation. Otherwise, a packet might
> arrive in between and the ESP rule would be deleted even though it
> was still active.

Good point. Didn't find a way to zero out individual rules though.

> > + *
> > + * @todo De-uglify this. You may be tempted to statically link in
> > libiptc,
> > + * and I'd generally approve of it because while it was
> > never meant
> > + * to be used publicly, quite some projects have relied on
> > it without
> > + * burning their fingers too badly for a long time now. But
> > on the
> > + * other hand, there's the impending nftables release that
> > will render
> > + * all your hard work obsolete anyway. I'd rather suggest
> > waiting for
> > + * a post-alpha release of libnl_nft before wasting your
> > time...
> > + * --cmroz, oct 2010
>
> [M] I find this confusing. First, the @todo says that this
> functionality should not be implemented as is but via libiptc
> instead. Then it says the implementation should stay the way it is.
> So is this really a @todo? Or is the @todo the update to libnl_nft?

The TODO applies to "de-uglify this", everything else is just a hint. Would you degrade it to a @note?

Alternatively, we could also discuss the issue right now and get rid of that paragraph altogether. Controlling iptables via shell may not be pretty, but using libiptc instead would blow up the code considerably in most cases, without real benefit. The only counterexample (that I know of), where libiptc could atually simplify matters, would be the ESP speedups introduced in this branch.

But pulling in a new lib because of that? Sounds like overkill.
Also, I bet there are many scripts out there that depend on the current output format, so the netfilter team will be careful not to change it unnecessarily.

Some months later, I'm not so sure anymore that iptables will be obsoleted in the near future.

> > + */
> > +static unsigned int detect_esp_rule_activity(const char *const cmd,
> > + const time_t now)
> > +{
> > +
> > + unsigned int ret = 0;
> > + bool chain_ok = false;
> > + char bfr[256];
> > + FILE *p;
> > +
> > + if (!(p = popen(cmd, "r"))) {
> > + HIP_ERROR("popen(\"%s\"): %s\n", cmd, strerror(errno));
> > + return 0;
> > + }
> > +
> > + ...

Read more...

Revision history for this message
Stefan Götz (stefan.goetz-deactivatedaccount) wrote : Posted in a previous version of this proposal

Hi Diego!

>> > +static int hip_fw_manage_esp_rule(const struct esp_tuple *const esp_tuple,
>> > +                                  const struct in6_addr *const dest, bool insert)
>>
>> [M] 'const bool insert' for const correctness
>
> What's the point of adding const to a non-pointer value passed
> to a function?

doc/HACKING encourages const correctness, including the case of
function parameters (regardless of whether they are pointers or not).
The given code violates this policy. That's why I remarked on it.
Maybe I misunderstood your question?

Stefan

Revision history for this message
Stefan Götz (stefan.goetz-deactivatedaccount) wrote : Posted in a previous version of this proposal
Download full text (3.8 KiB)

Hi Christof!

On Mon, Mar 28, 2011 at 3:46 PM, Christof Mroz <email address hidden> wrote:
> On Sun, 27 Mar 2011 21:54:13 +0000
> Stefan Götz <email address hidden> wrote:
>
>> > + * @return true if rule was valid and output was
>> > written,
>> > + * false otherwise
>>
>> [L] Again, when is a rule 'valid'?
>
> Explaining the input format in detail would be reciting the
> implementation, I think. It already states that rules set up by
> hip_fw_manage_esp_rule() are covered, which is the important info.

Yes, I agree. What I meant was: it is not obvious, that 'rule' == 'input'. Consequently, you can give this function perfectly valid output from 'iptables -nvL' and that function would still return false - simply because not all output lines from 'iptables -nvL' are rules. That, however does not become clear from the documentation. Why I'm such a PITA about documentation here is that it's fairly difficult to correctly interpret the documentation without having read the source code in this case. Something like '@return true if @a input could be parsed as an ESP rule and the output parameters were set to the according values, false otherwise' is all I need to understand this better.

>> [M] Reading and zeroing the packet counters should be done atomically
>> with a single command line invocation. Otherwise, a packet might
>> arrive in between and the ESP rule would be deleted even though it
>> was still active.
>
> Good point. Didn't find a way to zero out individual rules though.

Yepp, it's not possible for individual rules, but you can combine the '-L' option for iptables with the '-Z' option for a particular chain to read and reset the counters atomically.

>> [M] I find this confusing. First, the @todo says that this
>> functionality should not be implemented as is but via libiptc
>> instead. Then it says the implementation should stay the way it is.
>> So is this really a @todo? Or is the @todo the update to libnl_nft?
>
> The TODO applies to "de-uglify this", everything else is just a hint. Would you degrade it to a @note?
>
> Alternatively, we could also discuss the issue right now and get rid of that paragraph altogether. Controlling iptables via shell may not be pretty, but using libiptc instead would blow up the code considerably in most cases, without real benefit. The only counterexample (that I know of), where libiptc could atually simplify matters, would be the ESP speedups introduced in this branch.
>
> But pulling in a new lib because of that? Sounds like overkill.
> Also, I bet there are many scripts out there that depend on the current output format, so the netfilter team will be careful not to change it unnecessarily.
>
> Some months later, I'm not so sure anymore that iptables will be obsoleted in the near future.

Okay, now I have all the information :-) This should go into a @note in order to justify your decision for parsing iptables output so every other developer doesn't have to scratch their heads as to why this route was chosen. I don't think we need 'to do' anything about this code at all.

>> > + if (strncmp(bfr, "Chain", 5) == 0) {
>> > + chain_ok =...

Read more...

review: Needs Fixing
Revision history for this message
Stefan Götz (stefan.goetz-deactivatedaccount) wrote : Posted in a previous version of this proposal

Hi Christof!

> === modified file 'firewall/conntrack.c'
> +static void update_esp_address(struct esp_tuple *esp_tuple,

[M] would 'struct esp_tuple *const esp_tuple' work for better const correctness?

> /**
> + * Parse one line of iptables -nvL formatted output, and extract
> + * packet count, SPI and destination IP if it is valid and packet count is
> + * not zero.

[L] What is the acceptable/supported format? 'iptables -nvL' output might differ based on version or translation. Examples would be sufficient.

[M] The functions hip_fw_manage_esp_tuple() and system_printf() are not covered by unit tests.

review: Needs Fixing
Revision history for this message
Diego Biurrun (diego-biurrun) wrote : Posted in a previous version of this proposal

 review abstain

I'm fine with the merge proposal now, some minor comments below.
All you need to pass now is Stefan's review :)

On Mon, Mar 28, 2011 at 02:06:47PM +0000, Christof Mroz wrote:
> Christof Mroz has proposed merging lp:~christof-mroz/hipl/hipfw-esp-speedup into lp:hipl with lp:~christof-mroz/hipl/hipfw-timeout as a prerequisite.
>
> --- firewall/conntrack.c 2011-03-28 14:06:39 +0000
> +++ firewall/conntrack.c 2011-03-28 14:06:40 +0000
> @@ -1984,6 +2114,174 @@
>
> + if ((str_spi = strstr(input, "spi:"))) {
> + // non-UDP
> + if (sscanf(str_spi, "spi:%u", spi) < 1) {
> + HIP_ERROR("Unexpected iptables output (spi): '%s'\n", input);
> + return false;
> + }
> + } else if ((str_spi = strstr(input, u32_prefix))) {
> + // UDP
> + // spi follows u32_prefix string as a hex number
> + // (always host byte order)
> + if (sscanf(&str_spi[sizeof(u32_prefix) - 1], "%x", spi) < 1) {
> + HIP_ERROR("Unexpected iptables output (u32 match): '%s'\n", input);
> + return false;
> + }
> + } else {
> + // no SPI specified, so it's no ESP rule
> + return false;
> + }
> +
> + // grab packet count and destination IP.
> + if (sscanf(input, formats[0], packet_count, ip) < 2) {
> + // retry with alternative format before we give up
> + if (sscanf(input, formats[1], packet_count, ip) < 2) {
> + HIP_ERROR("Unexpected iptables output (number of colums): '%s'\n", input);
> + return false;
> + }
> + }

All the inner if-conditions could be folded into the outer if-conditions.

> + while (fgets(bfr, sizeof(bfr), p)) {
> + if (strncmp(bfr, "Chain", 5) == 0) {
> + chain_ok = (strncmp(bfr, "Chain HIPFW-INPUT", 17) == 0) ||
> + (strncmp(bfr, "Chain HIPFW-OUTPUT", 18) == 0) ||
> + (strncmp(bfr, "Chain HIPFW-FORWARD", 19) == 0);

nit: The () are unnecessary.

> --- firewall/pisa.c 2011-01-14 15:53:02 +0000
> +++ firewall/pisa.c 2011-03-28 14:06:40 +0000
> @@ -311,6 +311,12 @@
> if (t) {
> t->connection->pisa_state = PISA_STATE_ALLOW;
> HIP_INFO("PISA accepted the connection.\n");
> +
> + struct slist *lst = t->esp_tuples;
> + while (lst) {
> + hip_fw_manage_esp_tuple(lst->data, true);
> + lst = lst->next;
> + }
> } else {
> HIP_ERROR("Connection not found.\n");
> }
> @@ -329,6 +335,15 @@
> if (t) {
> t->connection->pisa_state = PISA_STATE_DISALLOW;
> + HIP_INFO("PISA removed the connection.\n");
> +
> + struct slist *lst = t->esp_tuples;
> + while (lst) {
> + hip_fw_manage_esp_tuple(lst->data, false);
> + lst = lst->next;
> + }
> + } else {
> + HIP_ERROR("Connection not found.\n");
> }
> }

This might be worth moving into its own function to avoid the
code duplication.

Diego

review: Abstain
Revision history for this message
Diego Biurrun (diego-biurrun) wrote : Posted in a previous version of this proposal

On Mon, Mar 28, 2011 at 03:50:51PM +0000, Stefan Götz wrote:
>
> >> > +static int hip_fw_manage_esp_rule(const struct esp_tuple *const esp_tuple,
> >> > +                                  const struct in6_addr *const dest, bool insert)
> >>
> >> [M] 'const bool insert' for const correctness
> >
> > What's the point of adding const to a non-pointer value passed
> > to a function?
>
> doc/HACKING encourages const correctness, including the case of
> function parameters (regardless of whether they are pointers or not).
> The given code violates this policy. That's why I remarked on it.

Yes, of course.

> Maybe I misunderstood your question?

I'm wondering aloud if it makes sense to enforce const correctness for
values passed to a function as ints. Modifying such values will not
have side effects since they are only used in the function.

So I'm wondering if it makes sense to demand const correctness for such
values or if it's just a pointless bother for the programmer.

Note that I don't have an answer, I'm wondering and I'm interested in
hearing opinions.

Diego

Revision history for this message
Stefan Götz (stefan.goetz-deactivatedaccount) wrote : Posted in a previous version of this proposal

> I'm wondering aloud if it makes sense to enforce const correctness for
> values passed to a function as ints.  Modifying such values will not
> have side effects since they are only used in the function.

1) True, a modified function argument has no side effect outside the function.

2) This is not about external side effects. This is about keeping a
variable value constant within its scope if it is *intended* not to
change. By extension of your argument (and to annoy you a bit), we
don't need to mark global constants as 'const' anymore because these
'const's are a bother to everyone as long as they either painstakingly
check that these intended constants are never modified during runtime.
Or because such a modification has no side effects outside the running
process anyway.

> Note that I don't have an answer, I'm wondering and I'm interested in
> hearing opinions.

My opinion, I guess, is clear :) When an L-Value (variable/argument)
can be const, it should be const. I think it is good programming
practice and a good policy to let the compiler help where it can.

Stefan

Revision history for this message
Christof Mroz (christof-mroz) wrote : Posted in a previous version of this proposal

On Mon, 28 Mar 2011 18:11:00 +0200, Stefan Götz
<email address hidden> wrote:

>>> [L] Again, when is a rule 'valid'?
>>
>> Explaining the input format in detail would be reciting the
>> implementation, I think. It already states that rules set up by
>> hip_fw_manage_esp_rule() are covered, which is the important info.
>
> Yes, I agree. What I meant was: it is not obvious, that 'rule' ==
> 'input'.

Ah I see what you meant in the last comment now.

> Something like '@return true if @a input could be parsed as an ESP
> rule and the output parameters were set to the according values, false
> otherwise'

Thanks, I'll plagiarize that.

Revision history for this message
Christof Mroz (christof-mroz) wrote : Posted in a previous version of this proposal

On Mon, 28 Mar 2011 19:03:34 +0200, Diego Biurrun <email address hidden> wrote:

> I'm fine with the merge proposal now, some minor comments below.
> All you need to pass now is Stefan's review :)

Hooray! On to the final boss.

>> + // grab packet count and destination IP.
>> + if (sscanf(input, formats[0], packet_count, ip) < 2) {
>> + // retry with alternative format before we give up
>> + if (sscanf(input, formats[1], packet_count, ip) < 2) {
>> + HIP_ERROR("Unexpected iptables output (number of colums):
>> '%s'\n", input);
>> + return false;
>> + }
>> + }
>
> All the inner if-conditions could be folded into the outer if-conditions.

It's more readable this way IMO.

>> --- firewall/pisa.c 2011-01-14 15:53:02 +0000
>> +++ firewall/pisa.c 2011-03-28 14:06:40 +0000
>> @@ -311,6 +311,12 @@
>> if (t) {
>> t->connection->pisa_state = PISA_STATE_ALLOW;
>> HIP_INFO("PISA accepted the connection.\n");
>> +
>> + struct slist *lst = t->esp_tuples;
>> + while (lst) {
>> + hip_fw_manage_esp_tuple(lst->data, true);
>> + lst = lst->next;
>> + }
>> } else {
>> HIP_ERROR("Connection not found.\n");
>> }
>> @@ -329,6 +335,15 @@
>> if (t) {
>> t->connection->pisa_state = PISA_STATE_DISALLOW;
>> + HIP_INFO("PISA removed the connection.\n");
>> +
>> + struct slist *lst = t->esp_tuples;
>> + while (lst) {
>> + hip_fw_manage_esp_tuple(lst->data, false);
>> + lst = lst->next;
>> + }
>> + } else {
>> + HIP_ERROR("Connection not found.\n");
>> }
>> }
>
> This might be worth moving into its own function to avoid the
> code duplication.

OK, Good idea.

Revision history for this message
Diego Biurrun (diego-biurrun) wrote : Posted in a previous version of this proposal

On Tue, Mar 29, 2011 at 07:30:58PM +0000, Christof Mroz wrote:
> On Mon, 28 Mar 2011 19:03:34 +0200, Diego Biurrun <email address hidden> wrote:
>
> > I'm fine with the merge proposal now, some minor comments below.
> > All you need to pass now is Stefan's review :)
>
> Hooray! On to the final boss.

:)

> >> + // grab packet count and destination IP.
> >> + if (sscanf(input, formats[0], packet_count, ip) < 2) {
> >> + // retry with alternative format before we give up
> >> + if (sscanf(input, formats[1], packet_count, ip) < 2) {
> >> + HIP_ERROR("Unexpected iptables output (number of colums):
> >> '%s'\n", input);
> >> + return false;
> >> + }
> >> + }
> >
> > All the inner if-conditions could be folded into the outer if-conditions.
>
> It's more readable this way IMO.

I disagree, you also get rid of one level of indentation and semantically
it's one big condition, not two where something might also happen if the
second is not fulfilled. Use your own judgement...

Diego

Revision history for this message
Stefan Götz (stefan.goetz-deactivatedaccount) wrote :

Hi Christof!

> === modified file 'firewall/conntrack.c'
> --- firewall/conntrack.c 2011-04-04 16:39:33 +0000
> +++ firewall/conntrack.c 2011-04-04 16:39:33 +0000
> @@ -106,6 +110,8 @@
> STATE_CLOSING
> };
>
> +static unsigned int total_esp_rules_count = 0;

[L] A short comment about where/how this variable is necessary or helpful would be useful to readers.

> + while (fgets(bfr, sizeof(bfr), p)) {
> + unsigned int packet_count;
> + uint32_t spi;
> + struct in6_addr dest;
> + struct tuple *tuple;
> +
> + if (parse_iptables_esp_rule(bfr, &packet_count, &spi, &dest)) {
> + ret += 1;
> + if (packet_count > 0) {
> + tuple = get_tuple_by_esp(&dest, spi);
> + if (!tuple) {
> + HIP_ERROR("Stray ESP rule: SPI = %u\n", spi);
> + continue;
> + }
> +
> + tuple->connection->timestamp = now;
> + HIP_DEBUG("Activity detected: SPI = %u\n", spi);
> + HIP_DEBUG_IN6ADDR("dest: ", &dest);
> + }
> + }
> + }

[L] could 'tuple' be declared within the 'if (packet_count > 0) {' scope?

Stefan

review: Approve
5810. By Christof Mroz

Declare tuple variable at tighter scope.

5811. By Christof Mroz

Document static global total_esp_rules_count.

Revision history for this message
René Hummen (rene-hummen) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'Makefile.am'
2--- Makefile.am 2011-04-04 22:33:36 +0000
3+++ Makefile.am 2011-04-04 22:33:36 +0000
4@@ -201,6 +201,7 @@
5 test/mocks.c \
6 test/firewall/conntrack.c \
7 test/firewall/file_buffer.c \
8+ test/firewall/helpers.c \
9 test/firewall/line_parser.c \
10 test/firewall/port_bindings.c \
11 $(firewall_hipfw_sources)
12
13=== modified file 'firewall/conntrack.c'
14--- firewall/conntrack.c 2011-04-04 22:33:36 +0000
15+++ firewall/conntrack.c 2011-04-04 22:33:36 +0000
16@@ -40,7 +40,9 @@
17 #define _BSD_SOURCE
18
19 #include <errno.h>
20+#include <limits.h>
21 #include <stdint.h>
22+#include <stdbool.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <arpa/inet.h>
26@@ -50,6 +52,7 @@
27 #include <openssl/dsa.h>
28 #include <openssl/rsa.h>
29 #include <sys/time.h>
30+#include <linux/netfilter_ipv4.h>
31
32 #include "lib/core/builder.h"
33 #include "lib/core/debug.h"
34@@ -62,6 +65,7 @@
35 #include "modules/update/hipd/update.h"
36 #include "common_types.h"
37 #include "dlist.h"
38+#include "hslist.h"
39 #include "esp_prot_conntrack.h"
40 #include "firewall_defines.h"
41 #include "firewall.h"
42@@ -106,6 +110,12 @@
43 STATE_CLOSING
44 };
45
46+/**
47+ * Number of currently managed iptables rules for ESP speedup (-u option).
48+ * @see hip_fw_manage_esp_rule();
49+ */
50+static unsigned int total_esp_rules_count = 0;
51+
52 /*------------print functions-------------*/
53 /**
54 * prints out the list of addresses of esp_addr_list
55@@ -341,6 +351,161 @@
56 }
57
58 /**
59+ * Set up or remove iptables rules to bypass userspace processing of the
60+ * SPI/destination pairs as specified by @a esp_tuple and @a dest.
61+ * This can greatly improve firewall throughput.
62+ *
63+ * @param esp_tuple Determines the SPI.
64+ * @param dest The corresponding destination address to bypass. May be
65+ * a IPv6-mapped IPv4 address.
66+ * @param insert Insert new rule if true, remove existing if false.
67+ * @return 0 if rules were modified, -1 otherwise.
68+ *
69+ * @note This feature may be turned off completely by the -u command line option.
70+ * It is also automatically deactivated for connections that demand
71+ * more advanced connection tracking.
72+ * In these cases, -1 is returned even though there was not even an
73+ * attempt to modify rules.
74+ *
75+ * @note This interferes, in one way or another, with userspace_ipsec,
76+ * relay, LSI, midauth, lightweight-update and esp_prot. Care was
77+ * taken to not break these features though.
78+ *
79+ * @see update_esp_address()
80+ * @see free_esp_tuple()
81+ * @see ::esp_speedup
82+ */
83+static int hip_fw_manage_esp_rule(const struct esp_tuple *const esp_tuple,
84+ const struct in6_addr *const dest,
85+ const bool insert)
86+{
87+ int err = 0;
88+ const char *flag = insert ? "-I" : "-D";
89+ const char *table = NULL;
90+
91+ if (!esp_speedup || hip_userspace_ipsec) {
92+ return -1;
93+ }
94+
95+ HIP_ASSERT(esp_tuple);
96+ HIP_ASSERT(dest);
97+
98+ if (esp_tuple->esp_prot_tfm > ESP_PROT_TFM_UNUSED) {
99+ HIP_DEBUG("ESP Transforms requested; not handled via iptables "
100+ "since we need to inspect packets\n");
101+ return -1;
102+ }
103+
104+ if (esp_tuple->tuple->esp_relay) {
105+ HIP_DEBUG("ESP Relay requested; not handled via iptables "
106+ "since we need packet rewriting\n");
107+ return -1;
108+ }
109+
110+ switch (esp_tuple->tuple->hook) {
111+ case NF_IP_LOCAL_IN:
112+ table = "HIPFW-INPUT";
113+ break;
114+ case NF_IP_FORWARD:
115+ table = "HIPFW-FORWARD";
116+ break;
117+ case NF_IP_LOCAL_OUT:
118+ table = "HIPFW-OUTPUT";
119+ break;
120+ default:
121+ HIP_ERROR("Packet was received via unsupported netfilter hook %d\n",
122+ esp_tuple->tuple->hook);
123+ return -1;
124+ }
125+
126+ HIP_DEBUG("insert = %d\n", insert);
127+ HIP_DEBUG("table = %s\n", table);
128+ HIP_DEBUG("esp_tuple->spi = 0x%08X\n", esp_tuple->spi);
129+ HIP_DEBUG_IN6ADDR("src ip", esp_tuple->tuple->src_ip);
130+ HIP_DEBUG_IN6ADDR("dest ip", dest);
131+
132+ if (IN6_IS_ADDR_V4MAPPED(dest)) {
133+ char daddr[INET_ADDRSTRLEN];
134+ struct in_addr dest4;
135+
136+ IPV6_TO_IPV4_MAP(dest, &dest4);
137+ HIP_IFEL(!inet_ntop(AF_INET, &dest4, daddr, sizeof(daddr)), -1,
138+ "inet_ntop: %s\n", strerror(errno));
139+
140+ if (esp_tuple->tuple->connection->udp_encap) {
141+ /* SPI is the first 32bit value in encapsulating UDP payload, so
142+ * we may use a simple u32 Pattern. Here, '4&0x1FFF=0' ensures
143+ * we're not processing a fragmented packet.
144+ */
145+ err = system_printf("iptables %s %s -p UDP "
146+ "--dport 10500 --sport 10500 -d %s -m u32 "
147+ "--u32 '4&0x1FFF=0 && 0>>22&0x3C@8=0x%08X' -j ACCEPT",
148+ flag, table, daddr, esp_tuple->spi);
149+ } else {
150+ err = system_printf("iptables %s %s -p 50 "
151+ "-d %s -m esp --espspi 0x%08X -j ACCEPT",
152+ flag, table, daddr, esp_tuple->spi);
153+ }
154+ } else {
155+ char daddr[INET6_ADDRSTRLEN];
156+ HIP_IFEL(!inet_ntop(AF_INET6, dest, daddr, sizeof(daddr)), -1,
157+ "inet_ntop: %s\n", strerror(errno));
158+
159+ HIP_ASSERT(!esp_tuple->tuple->connection->udp_encap);
160+ err = system_printf("ip6tables %s %s -p 50 "
161+ "-d %s -m esp --espspi 0x%08X -j ACCEPT",
162+ flag, table, daddr, esp_tuple->spi);
163+ }
164+
165+ if (err == EXIT_SUCCESS) {
166+ total_esp_rules_count += (insert ? 1 : -1);
167+ HIP_DEBUG("total_esp_rules_count = %d\n", total_esp_rules_count);
168+ }
169+
170+out_err:
171+ return err == EXIT_SUCCESS ? 0 : -1;
172+}
173+
174+/**
175+ * Set up or remove iptables rules to bypass userspace processing of all
176+ * SPI/destination pairs associated with @a esp_tuple.
177+ *
178+ * @param esp_tuple Determines the SPI and all destination addresses.
179+ * @param insert Insert rules if true, remove existing if false.
180+ *
181+ * @see hip_fw_manage_esp_rule()
182+ */
183+static void hip_fw_manage_esp_tuple(const struct esp_tuple *const esp_tuple,
184+ const bool insert)
185+{
186+ const struct hip_ll_node *node = esp_tuple->dst_addresses.head;
187+ while (node) {
188+ hip_fw_manage_esp_rule(esp_tuple, node->ptr, insert);
189+ node = node->next;
190+ }
191+}
192+
193+/**
194+ * Set up or remove iptables rules to bypass userspace processing of all
195+ * ESP SPI/destination pairs associated with @a tuple.
196+ *
197+ * @param esp_tuple Determines all SPI/destination pairs.
198+ * @param insert Insert rules if true, remove existing if false.
199+ *
200+ * @see hip_fw_manage_esp_rule()
201+ * @see hip_fw_manage_esp_tuple()
202+ */
203+void hip_fw_manage_all_esp_tuples(const struct tuple *const tuple,
204+ const bool insert)
205+{
206+ const struct slist *lst = tuple->esp_tuples;
207+ while (lst) {
208+ hip_fw_manage_esp_tuple(lst->data, insert);
209+ lst = lst->next;
210+ }
211+}
212+
213+/**
214 * Insert or update a destination address associated with an ESP tuple.
215 * If the address is already known, its update_id is replaced with the new
216 * value.
217@@ -383,6 +548,7 @@
218 *esp_addr->update_id = *upd_id;
219 }
220
221+ hip_fw_manage_esp_rule(esp_tuple, addr, true);
222 return true;
223
224 out_err:
225@@ -458,12 +624,17 @@
226 }
227
228 /**
229- * initialize and store a new HIP/ESP connnection into the connection table
230- *
231- * @param data the connection-related data to be inserted
232- * @see remove_connection
233+ * Initialize and store a new HIP/ESP connnection into the connection
234+ * table.
235+ *
236+ * @param data The connection-related data to be inserted.
237+ * @param ctx The packet context. Note that source and destination HITs
238+ * are always taken from @a data rather than @a ctx.
239+ *
240+ * @see remove_connection()
241 */
242-static void insert_new_connection(const struct hip_data *data)
243+static void insert_new_connection(const struct hip_data *const data,
244+ const struct hip_fw_context *const ctx)
245 {
246 struct connection *connection = NULL;
247
248@@ -472,6 +643,7 @@
249 connection = calloc(1, sizeof(struct connection));
250
251 connection->state = STATE_ESTABLISHED;
252+ connection->udp_encap = ctx->udp_encap_hdr ? true : false;
253 connection->timestamp = time(NULL);
254 #ifdef HIP_CONFIG_MIDAUTH
255 connection->pisa_state = PISA_STATE_DISALLOW;
256@@ -568,6 +740,7 @@
257
258 // remove all associated addresses
259 while ((addr = hip_ll_del_first(&esp_tuple->dst_addresses, NULL))) {
260+ hip_fw_manage_esp_rule(esp_tuple, &addr->dst_addr, false);
261 free(addr->update_id);
262 free(addr);
263 }
264@@ -1102,6 +1275,7 @@
265
266 other_dir->esp_tuples = append_to_slist(other_dir->esp_tuples, esp_tuple);
267
268+ update_esp_address(esp_tuple, ip6_src, NULL);
269 insert_esp_tuple(esp_tuple);
270 }
271
272@@ -1171,6 +1345,7 @@
273 -1, "adding or updating ESP destination address failed");
274 esp_tuple->tuple = other_dir;
275
276+ update_esp_address(esp_tuple, ip6_src, NULL);
277 insert_esp_tuple(esp_tuple);
278
279 HIP_DEBUG("ESP tuple inserted\n");
280@@ -1663,7 +1838,7 @@
281 }
282 #endif
283
284- insert_new_connection(data);
285+ insert_new_connection(data, ctx);
286
287 // TODO call free for all pointer members of data - comment by Rene
288 free(data);
289@@ -1720,6 +1895,8 @@
290 } else {
291 HIP_DEBUG("Tuple connection NULL, could not timestamp\n");
292 }
293+
294+ tuple->hook = ctx->ipq_packet->hook;
295 }
296
297 HIP_DEBUG("udp_encap_hdr=%p tuple=%p err=%d\n", ctx->udp_encap_hdr, tuple, err);
298@@ -2022,6 +2199,177 @@
299 }
300
301 /**
302+ * Parse one line of `iptables -nvL` formatted output and extract packet count,
303+ * SPI and destination IP if the line corresponds to a previously set up ESP
304+ * rule.
305+ * This takes into account specifically the kinds of rules that can be created
306+ * by hip_fw_manage_esp_rule().
307+ *
308+ * @param input The line to be parsed.
309+ * @param packet_count Out: receives the packet count.
310+ * @param spi Out: receives the SPI, unless the packet count was zero.
311+ * @param dest Out: receives the destination IP, unless the packet count
312+ * was zero.
313+ * @return true if @a input could be parsed as an ESP
314+ * rule (and at least @a packet_count was set), false
315+ * otherwise.
316+ *
317+ * @note Short-circuiting behaviour for @a spi and @a dest (see description).
318+ *
319+ * @see detect_esp_rule_activity()
320+ * @see hip_fw_manage_esp_rule()
321+ */
322+static bool parse_iptables_esp_rule(const char *const input,
323+ unsigned int *const packet_count,
324+ uint32_t *const spi,
325+ struct in6_addr *const dest)
326+{
327+ static const char u32_prefix[] = "u32 0x4&0x1fff=0x0&&0x0>>0x16&0x3c@0x8=0x";
328+
329+ /*
330+ * In iptables output, one column is optional. So we try the long
331+ * format first and fall back to the shorter one (see sscanf call
332+ * below).
333+ * The %45s format is used here because 45 is the maximum IPv6 address
334+ * length, considering all variations (i.e. INET6_ADDRSTRLEN - 1).
335+ */
336+ static const char *formats[] = { "%u %*u %*s %*s %*2[!f-] %*s %*s %*s %45s",
337+ "%u %*u %*s %*s %*s %*s %*s %45s" };
338+
339+ char ip[INET6_ADDRSTRLEN];
340+ const char *str_spi;
341+
342+ // there's two ways of specifying SPIs in a rule
343+ // (see hip_fw_manage_esp_rule)
344+
345+ if ((str_spi = strstr(input, "spi:"))) {
346+ // non-UDP
347+ if (sscanf(str_spi, "spi:%u", spi) < 1) {
348+ HIP_ERROR("Unexpected iptables output (spi): '%s'\n", input);
349+ return false;
350+ }
351+ } else if ((str_spi = strstr(input, u32_prefix))) {
352+ // UDP
353+ // spi follows u32_prefix string as a hex number
354+ // (always host byte order)
355+ if (sscanf(&str_spi[sizeof(u32_prefix) - 1], "%x", spi) < 1) {
356+ HIP_ERROR("Unexpected iptables output (u32 match): '%s'\n", input);
357+ return false;
358+ }
359+ } else {
360+ // no SPI specified, so it's no ESP rule
361+ return false;
362+ }
363+
364+ // grab packet count and destination IP.
365+ if (sscanf(input, formats[0], packet_count, ip) < 2) {
366+ // retry with alternative format before we give up
367+ if (sscanf(input, formats[1], packet_count, ip) < 2) {
368+ HIP_ERROR("Unexpected iptables output (number of colums): '%s'\n", input);
369+ return false;
370+ }
371+ }
372+
373+ // IP not needed, unless there was activity
374+ if (*packet_count > 0) {
375+ char *slash;
376+
377+ // IP may be in /128 format, strip the suffix
378+ if ((slash = strchr(ip, '/'))) {
379+ *slash = '\0';
380+ }
381+
382+ // parse destination IP; try IPv6 first, then IPv4
383+ if (!inet_pton(AF_INET6, ip, dest)) {
384+ struct in_addr addr4;
385+ if (!inet_pton(AF_INET, ip, &addr4)) {
386+ HIP_ERROR("Unexpected iptables output: '%s'\n", input);
387+ HIP_ERROR("Can't parse destination IP: %s\n", ip);
388+ return false;
389+ }
390+
391+ IPV4_TO_IPV6_MAP(&addr4, dest);
392+ }
393+ }
394+
395+ return true;
396+}
397+
398+/**
399+ * Update timestamps of all ESP tuples where corresponding iptables rules'
400+ * packet counters are non-zero.
401+ * Currently, this works by parsing the output of iptables and ip6tables
402+ * to extract and zero the packet counters.
403+ *
404+ * @param now We consider this the current time.
405+ * @return Number of rules that were identified with an esp tuple
406+ * (not necessarily the number of tuples updated), or -1 if
407+ * communication with iptables failed.
408+ *
409+ * @note Ugly approach, yes. You may be tempted to statically link in
410+ * libiptc, and I'd generally approve of it because while it was never
411+ * meant to be used publicly, quite some projects have relied on it
412+ * without burning their fingers too badly for a long time now. On the
413+ * other hand, libiptc would blow up iptables communication code at most
414+ * other places, and the output format is unlikely to change.
415+ * Furthermore, a successor to iptables (namely: nftables) with an actual
416+ * API is under consideration by the netfilter team.
417+ */
418+static int detect_esp_rule_activity(const time_t now)
419+{
420+ static const char *const bins[] = { "iptables", "ip6tables" };
421+ static const char *const chains[] = { "HIPFW-INPUT", "HIPFW-OUTPUT",
422+ "HIPFW-FORWARD" };
423+
424+ unsigned int chain, bin, ret = 0;
425+
426+ for (bin = 0; bin < ARRAY_SIZE(bins); ++bin) {
427+ for (chain = 0; chain < ARRAY_SIZE(chains); ++chain) {
428+ char bfr[256];
429+ FILE *p;
430+
431+ snprintf(bfr, sizeof(bfr), "%s -nvL -Z %s", bins[bin], chains[chain]);
432+ if (!(p = popen(bfr, "r"))) {
433+ HIP_ERROR("popen(\"%s\"): %s\n", bfr, strerror(errno));
434+ return -1;
435+ }
436+
437+ while (fgets(bfr, sizeof(bfr), p)) {
438+ unsigned int packet_count;
439+ uint32_t spi;
440+ struct in6_addr dest;
441+
442+ if (parse_iptables_esp_rule(bfr, &packet_count, &spi, &dest)) {
443+ ret += 1;
444+ if (packet_count > 0) {
445+ struct tuple *const tuple = get_tuple_by_esp(&dest, spi);
446+ if (!tuple) {
447+ HIP_ERROR("Stray ESP rule: SPI = %u\n", spi);
448+ continue;
449+ }
450+
451+ tuple->connection->timestamp = now;
452+ HIP_DEBUG("Activity detected: SPI = %u\n", spi);
453+ HIP_DEBUG_IN6ADDR("dest: ", &dest);
454+ }
455+ }
456+ }
457+
458+ if (!feof(p)) {
459+ HIP_ERROR("fgets(), bin: %s, chain %s: %s\n",
460+ bins[bin], chains[chain], strerror(errno));
461+ return -1;
462+ }
463+
464+ pclose(p);
465+ }
466+ }
467+
468+ HIP_DEBUG("-> %u\n", ret);
469+ return ret;
470+}
471+
472+/**
473 * Do some necessary bookkeeping concerning connection tracking.
474 * Currently, this only makes sure that stale locations will be removed.
475 * The actual tasks will be run at most once per ::connection_timeout
476@@ -2032,7 +2380,7 @@
477 */
478 void hip_fw_conntrack_periodic_cleanup(void)
479 {
480- static time_t last_check = 0; // timestamp of last call
481+ static time_t last_check = 0; // timestamp of last call
482 struct slist *iter_conn;
483 struct connection *conn;
484
485@@ -2052,6 +2400,17 @@
486 if (now - last_check >= cleanup_interval) {
487 HIP_DEBUG("Checking for connection timeouts\n");
488
489+ // If connections are covered by iptables rules, we rely on kernel
490+ // packet counters to update timestamps indirectly for these.
491+
492+ if (total_esp_rules_count > 0) {
493+ // cast to signed value
494+ const int found = detect_esp_rule_activity(now);
495+ if (found == -1 || (unsigned int) found != total_esp_rules_count) {
496+ HIP_ERROR("Not all ESP tuples' packet counts were found\n");
497+ }
498+ }
499+
500 iter_conn = conn_list;
501 while (iter_conn) {
502 conn = iter_conn->data;
503
504=== modified file 'firewall/conntrack.h'
505--- firewall/conntrack.h 2011-04-04 22:33:36 +0000
506+++ firewall/conntrack.h 2011-04-04 22:33:36 +0000
507@@ -29,6 +29,7 @@
508 #define _BSD_SOURCE
509
510 #include <stdint.h>
511+#include <stdbool.h>
512 #include <netinet/in.h>
513
514 #include "lib/core/protodefs.h"
515@@ -63,6 +64,8 @@
516 const struct in6_addr *dst_hit);
517 int hipfw_relay_esp(const struct hip_fw_context *ctx);
518
519+void hip_fw_manage_all_esp_tuples(const struct tuple *const tuple,
520+ const bool insert);
521 void hip_fw_conntrack_periodic_cleanup(void);
522
523 #endif /* HIP_FIREWALL_CONNTRACK_H */
524
525=== modified file 'firewall/firewall.c'
526--- firewall/firewall.c 2011-04-04 22:33:36 +0000
527+++ firewall/firewall.c 2011-04-04 22:33:36 +0000
528@@ -144,6 +144,7 @@
529 int hip_lsi_support = 0;
530 int esp_relay = 0;
531 int hip_esp_protection = 0;
532+int esp_speedup = 0; /**< Enable esp speedup via dynamic iptables usage (-u option). */
533 #ifdef CONFIG_HIP_MIDAUTH
534 int use_midauth = 0;
535 #endif
536
537=== modified file 'firewall/firewall.h'
538--- firewall/firewall.h 2011-03-22 10:31:37 +0000
539+++ firewall/firewall.h 2011-04-04 22:33:36 +0000
540@@ -47,6 +47,7 @@
541 extern int hip_fw_sock;
542 extern int hip_fw_async_sock;
543 extern int system_based_opp_mode;
544+extern int esp_speedup;
545
546 int hipfw_main(const char *const rule_file,
547 const bool kill_old,
548
549=== modified file 'firewall/firewall_defines.h'
550--- firewall/firewall_defines.h 2011-04-04 22:33:36 +0000
551+++ firewall/firewall_defines.h 2011-04-04 22:33:36 +0000
552@@ -30,6 +30,7 @@
553
554 #include <libipq.h>
555 #include <stdint.h>
556+#include <stdbool.h>
557 #include <netinet/in.h>
558 #include <netinet/ip6.h>
559 #include <netinet/tcp.h>
560@@ -129,6 +130,7 @@
561 int direction;
562 struct connection *connection;
563 int state;
564+ int hook; /**< iptables chain this tuple originates from. */
565 uint32_t lupdate_seq;
566 int esp_relay;
567 struct in6_addr esp_relay_daddr;
568@@ -141,6 +143,8 @@
569 int verify_responder;
570 int state;
571 time_t timestamp;
572+ /* members needed for iptables setup */
573+ bool udp_encap; /**< UDP encapsulation enabled? (NAT extension) */
574 /* members needed for ESP protection extension */
575 int num_esp_prot_tfms;
576 uint8_t esp_prot_tfms[MAX_NUM_TRANSFORMS];
577
578=== modified file 'firewall/helpers.c'
579--- firewall/helpers.c 2011-01-11 13:59:46 +0000
580+++ firewall/helpers.c 2011-04-04 22:33:36 +0000
581@@ -33,6 +33,8 @@
582 */
583
584 #include <stdlib.h>
585+#include <stdio.h>
586+#include <stdarg.h>
587 #include <arpa/inet.h>
588 #include <netinet/in.h>
589
590@@ -79,15 +81,58 @@
591 }
592
593 /**
594- * Executes a system command and prints an error if
595- * command wasn't successfull.
596+ * Executes a command and prints an error if command wasn't successful.
597 *
598- * @param command The system command. The caller of this function must take
599+ * @param command The command. The caller of this function must take
600 * care that command does not contain malicious code.
601- */
602-void system_print(const char *command)
603-{
604- if (system(command) == -1) {
605- HIP_ERROR("Could not execute system command %s", command);
606- }
607+ * @return Exit code on success, -1 on failure.
608+ */
609+int system_print(const char *const command)
610+{
611+ int ret;
612+
613+ if ((ret = system(command)) == -1) {
614+ HIP_ERROR("Could not execute command `%s'", command);
615+ return -1;
616+ }
617+
618+ HIP_DEBUG("$ %s -> %d\n", command, WEXITSTATUS(ret));
619+
620+ return WEXITSTATUS(ret);
621+}
622+
623+/**
624+ * printf()-like wrapper around system_print.
625+ * Fails and returns an error if the resulting command line
626+ * would be longer than ::MAX_COMMAND_LINE characters.
627+ *
628+ * @param command The command. This is a printf format string.
629+ * The caller of this function must take care that command
630+ * does not contain malicious code.
631+ * @return Exit code on success, -1 on failure.
632+ */
633+int system_printf(const char *const command, ...)
634+{
635+ char bfr[MAX_COMMAND_LINE + 1];
636+
637+ va_list vargs;
638+ va_start(vargs, command);
639+
640+ const int ret = vsnprintf(bfr, sizeof(bfr), command, vargs);
641+ if (ret <= 0) {
642+ HIP_ERROR("vsnprintf failed\n");
643+ va_end(vargs);
644+ return -1;
645+ }
646+
647+ // cast to unsigned value (we know that ret >= 0)
648+ if ((unsigned) ret > MAX_COMMAND_LINE) {
649+ HIP_ERROR("Format '%s' results in unexpectedly large command line "
650+ "(%d characters): not executed.\n", command, ret);
651+ va_end(vargs);
652+ return -1;
653+ }
654+
655+ va_end(vargs);
656+ return system_print(bfr);
657 }
658
659=== modified file 'firewall/helpers.h'
660--- firewall/helpers.h 2010-10-15 15:29:14 +0000
661+++ firewall/helpers.h 2011-04-04 22:33:36 +0000
662@@ -30,6 +30,10 @@
663
664 const char *addr_to_numeric(const struct in6_addr *addrp);
665 struct in6_addr *numeric_to_addr(const char *num);
666-void system_print(const char *command);
667+int system_print(const char *command);
668+int system_printf(const char *command, ...);
669+
670+/** The maximum command line length (that is, argument to system()) we expect. */
671+#define MAX_COMMAND_LINE 196
672
673 #endif /* HIP_FIREWALL_HELPERS_H */
674
675=== modified file 'firewall/main.c'
676--- firewall/main.c 2011-04-04 22:33:36 +0000
677+++ firewall/main.c 2011-04-04 22:33:36 +0000
678@@ -53,7 +53,7 @@
679 static void hipfw_usage(void)
680 {
681 puts("HIP Firewall");
682- puts("Usage: hipfw [-f file_name] [-d|-v] [-A] [-F] [-H] [-b] [-a] [-c] [-k] [-i|-I|-e] [-l] [-o] [-p] [-t <seconds>] [-h] [-V]");
683+ puts("Usage: hipfw [-f file_name] [-d|-v] [-A] [-F] [-H] [-b] [-a] [-c] [-k] [-i|-I|-e] [-l] [-o] [-p] [-t <seconds>] [-u] [-h] [-V]");
684 #ifdef CONFIG_HIP_MIDAUTH
685 puts(" [-m]");
686 #endif
687@@ -73,6 +73,7 @@
688 puts(" -l = activate lsi support");
689 puts(" -p = run with lowered privileges. iptables rules will not be flushed on exit");
690 puts(" -t <seconds> = set timeout interval to <seconds>. Disable if <seconds> = 0");
691+ puts(" -u = attempt to speed up esp traffic using iptables rules");
692 puts(" -h = print this help");
693 #ifdef CONFIG_HIP_MIDAUTH
694 puts(" -m = middlebox authentication");
695@@ -108,7 +109,7 @@
696 * Otherwise may get warnings from system_print() commands. */
697 setenv("PATH", HIP_DEFAULT_EXEC_PATH, 1);
698
699- while ((ch = getopt(argc, argv, "aAbcdef:FhHiIklmpt:vV")) != -1) {
700+ while ((ch = getopt(argc, argv, "aAbcdef:FhHiIklmpt:uvV")) != -1) {
701 switch (ch) {
702 case 'A':
703 accept_hip_esp_traffic_by_default = 1;
704@@ -171,6 +172,9 @@
705 cleanup_interval = connection_timeout;
706 }
707 break;
708+ case 'u':
709+ esp_speedup = 1;
710+ break;
711 case 'v':
712 log_level = LOGDEBUG_MEDIUM;
713 hip_set_logfmt(LOGFMT_SHORT);
714@@ -194,6 +198,25 @@
715 puts(" tracking disabled (-F)");
716 }
717
718+ if (esp_speedup && limit_capabilities) {
719+ puts("Conflict: ESP speedups (-u) requires root privileges,\n");
720+ puts(" but lowered privleges (-p) requested as well.\n");
721+ hipfw_usage();
722+ return EXIT_FAILURE;
723+ }
724+
725+ if (esp_speedup && hip_userspace_ipsec) {
726+ puts("Conflict: Bypassing userspace ESP processing (-u) impossible\n");
727+ puts(" with userspace IPSEC enabled (-i or -I)\n");
728+ hipfw_usage();
729+ return EXIT_FAILURE;
730+ }
731+
732+ if (esp_speedup && !filter_traffic) {
733+ puts("Warning: ESP speedup (-U) has no effect without\n");
734+ puts(" connection tracking (-F)\n");
735+ }
736+
737 if (geteuid() != 0) {
738 HIP_ERROR("Firewall must be run as root\n");
739 exit(-1);
740
741=== modified file 'firewall/pisa.c'
742--- firewall/pisa.c 2011-01-14 15:53:02 +0000
743+++ firewall/pisa.c 2011-04-04 22:33:36 +0000
744@@ -311,6 +311,7 @@
745 if (t) {
746 t->connection->pisa_state = PISA_STATE_ALLOW;
747 HIP_INFO("PISA accepted the connection.\n");
748+ hip_fw_manage_all_esp_tuples(t, true);
749 } else {
750 HIP_ERROR("Connection not found.\n");
751 }
752@@ -329,6 +330,10 @@
753
754 if (t) {
755 t->connection->pisa_state = PISA_STATE_DISALLOW;
756+ HIP_INFO("PISA removed the connection.\n");
757+ hip_fw_manage_all_esp_tuples(t, false);
758+ } else {
759+ HIP_ERROR("Connection not found.\n");
760 }
761 }
762
763
764=== modified file 'test/check_firewall.c'
765--- test/check_firewall.c 2011-04-04 22:33:36 +0000
766+++ test/check_firewall.c 2011-04-04 22:33:36 +0000
767@@ -31,8 +31,10 @@
768 int main(void)
769 {
770 int number_failed;
771- SRunner *sr = srunner_create(firewall_file_buffer());
772+ SRunner *sr = srunner_create(NULL);
773 srunner_add_suite(sr, firewall_conntrack());
774+ srunner_add_suite(sr, firewall_file_buffer());
775+ srunner_add_suite(sr, firewall_helpers());
776 srunner_add_suite(sr, firewall_line_parser());
777 srunner_add_suite(sr, firewall_port_bindings());
778
779
780=== modified file 'test/firewall/conntrack.c'
781--- test/firewall/conntrack.c 2011-04-04 22:33:36 +0000
782+++ test/firewall/conntrack.c 2011-04-04 22:33:36 +0000
783@@ -32,6 +32,8 @@
784 #include <check.h>
785 #include <signal.h>
786 #include <time.h>
787+#include <assert.h>
788+#include <stdlib.h>
789
790 #include "firewall/conntrack.h"
791 #include "firewall/conntrack.c"
792@@ -41,12 +43,13 @@
793
794 static struct connection *setup_connection(void)
795 {
796- struct hip_data data = { { { { 0 } } } };
797+ const struct hip_fw_context ctx = { 0 }; // only ctx.udp_encap_hdr is examined
798+ struct hip_data data = { { { { 0 } } } };
799
800 inet_pton(AF_INET6, "2001:12:bd2d:d23e:4a09:b2ab:6414:e110", &data.src_hit);
801 inet_pton(AF_INET6, "2001:10:f039:6bc5:cab3:0727:7fbc:9dcb", &data.dst_hit);
802
803- insert_new_connection(&data);
804+ insert_new_connection(&data, &ctx);
805
806 fail_if(conn_list == NULL, "No connection inserted.");
807 fail_if(conn_list->next != NULL, "More than one connection inserted.");
808@@ -54,6 +57,28 @@
809 return conn_list->data;
810 }
811
812+static struct esp_tuple *setup_esp_tuple(const uint32_t spi,
813+ const struct in6_addr *const dest,
814+ struct connection *const conn)
815+{
816+ struct esp_tuple *const esp_tuple = calloc(1, sizeof(*esp_tuple));
817+
818+ fail_if(conn == NULL, NULL);
819+ fail_if(esp_tuple == NULL, NULL);
820+
821+ esp_tuple->spi = spi;
822+ esp_tuple->tuple = &conn->original;
823+ update_esp_address(esp_tuple, dest, NULL);
824+ insert_esp_tuple(esp_tuple);
825+
826+ fail_if(esp_list == NULL, "Failed to insert a new ESP tuple");
827+
828+ conn->original.esp_tuples = append_to_slist(conn->original.esp_tuples,
829+ esp_tuple);
830+
831+ return esp_tuple;
832+}
833+
834 START_TEST(test_hip_fw_conntrack_periodic_cleanup_timeout)
835 {
836 struct connection *conn;
837@@ -128,14 +153,179 @@
838 }
839 END_TEST
840
841+START_TEST(test_parse_iptables_esp_rule)
842+{
843+ struct {
844+ const char *input;
845+ bool valid;
846+ unsigned int pkts;
847+ const char *ip;
848+ uint32_t spi;
849+ } test_cases[] = {
850+ { "Chain HIPFW-FORWARD (1 references)", .valid = false },
851+ { " pkts bytes target prot opt in out source destination ", .valid = false },
852+ { " 2 312 ACCEPT esp * * ::/0 3ffe:2::1/128 esp spi:469913213", .valid = true, .pkts = 2, .ip = "3ffe:2::1", .spi = 0x1c024e7d },
853+ { " 0 0 QUEUE udp * * ::/0 ::/0 udp spt:10500", .valid = false },
854+ { " 0 0 QUEUE esp * * ::/0 ::/0 ", .valid = false },
855+ { " 4 2264 QUEUE 139 * * ::/0 ::/0 ", .valid = false },
856+ { " 3 336 ACCEPT udp -- * * 0.0.0.0/0 192.168.2.1 udp spt:10500 dpt:10500 u32 0x4&0x1fff=0x0&&0x0>>0x16&0x3c@0x8=0xab772758", .valid = true, .pkts = 3, .ip = "::ffff:192.168.2.1", .spi = 0xab772758 }
857+ };
858+
859+ const int num_tests = sizeof(test_cases) / sizeof(*test_cases);
860+ int i;
861+
862+ for (i = 0; i < num_tests; ++i) {
863+ struct in6_addr dest, reference;
864+ unsigned int pkts;
865+ uint32_t spi;
866+
867+ if (parse_iptables_esp_rule(test_cases[i].input, &pkts, &spi, &dest)) {
868+ fail_unless(test_cases[i].valid, "Invalid rule was considered valid");
869+ fail_unless(pkts == test_cases[i].pkts, "Packet count not parsed correctly");
870+ fail_unless(spi == test_cases[i].spi, "SPI not parsed correctly");
871+
872+ assert(inet_pton(AF_INET6, test_cases[i].ip, &reference) == 1);
873+ fail_unless(IN6_ARE_ADDR_EQUAL(&dest, &reference),
874+ "Destination IP not parsed correctly.");
875+ } else {
876+ fail_unless(test_cases[i].valid == false, "Valid rule was considered invalid");
877+ }
878+ }
879+}
880+END_TEST
881+
882+START_TEST(test_hip_fw_manage_esp_rule_not_enabled)
883+{
884+ mock_system = true;
885+
886+ struct in6_addr dest;
887+ assert(inet_pton(AF_INET6, "3ffe::1", &dest));
888+
889+ struct connection *const conn = setup_connection();
890+ struct esp_tuple *const esp_tuple = setup_esp_tuple(0xAABBCCDD, &dest, conn);
891+
892+ esp_speedup = 0;
893+ conn->original.hook = NF_IP_LOCAL_IN;
894+
895+ fail_if(hip_fw_manage_esp_rule(esp_tuple, &dest, true) == 0,
896+ "Success, even though esp speedup is disabled");
897+ fail_if(mock_system_last, "Rule was created even though esp speedup is disabled");
898+}
899+END_TEST
900+
901+START_TEST(test_hip_fw_manage_esp_rule_needs_userspace)
902+{
903+ struct in6_addr dest;
904+ assert(inet_pton(AF_INET6, "3ffe::1", &dest));
905+
906+ struct connection *const conn = setup_connection();
907+ struct esp_tuple *const esp_tuple = setup_esp_tuple(0xAABBCCDD, &dest, conn);
908+
909+ esp_speedup = 1;
910+ conn->original.hook = NF_IP_LOCAL_IN;
911+
912+ esp_tuple->esp_prot_tfm = ESP_PROT_TFM_PLAIN;
913+ fail_if(hip_fw_manage_esp_rule(esp_tuple, &dest, true) == 0,
914+ "Added rule even though ESP transforms requested");
915+
916+ esp_tuple->esp_prot_tfm = ESP_PROT_TFM_UNUSED; // reset
917+ esp_tuple->tuple->esp_relay = 1;
918+ fail_if(hip_fw_manage_esp_rule(esp_tuple, &dest, true) == 0,
919+ "Added rule even though connection is relayed");
920+
921+ hip_userspace_ipsec = 1;
922+ fail_if(hip_fw_manage_esp_rule(esp_tuple, &dest, true) == 0,
923+ "Added rule even though userspace IPSEC requested");
924+}
925+END_TEST
926+
927+START_TEST(test_hip_fw_manage_esp_rule_inet6)
928+{
929+ mock_system = true;
930+
931+ static const char *const expected = "ip6tables -I HIPFW-INPUT -p 50 "
932+ "-d 3ffe::1 -m esp --espspi 0xAABBCCDD "
933+ "-j ACCEPT";
934+
935+ struct in6_addr dest;
936+ assert(inet_pton(AF_INET6, "3ffe::1", &dest));
937+
938+ struct connection *const conn = setup_connection();
939+ struct esp_tuple *const esp_tuple = setup_esp_tuple(0xAABBCCDD, &dest, conn);
940+
941+ esp_speedup = 1;
942+ hip_userspace_ipsec = 0;
943+ conn->original.hook = NF_IP_LOCAL_IN;
944+
945+ fail_if(hip_fw_manage_esp_rule(esp_tuple, &dest, true) != 0);
946+ fail_if(strcmp(mock_system_last, expected) != 0, "Unexpected rule was generated");
947+}
948+END_TEST
949+
950+START_TEST(test_hip_fw_manage_esp_rule_inet4)
951+{
952+ mock_system = true;
953+
954+ static const char *const expected = "iptables -I HIPFW-FORWARD -p 50 "
955+ "-d 192.168.1.1 -m esp --espspi 0xAABBCCDD "
956+ "-j ACCEPT";
957+
958+ struct in6_addr dest;
959+ assert(inet_pton(AF_INET6, "::ffff:192.168.1.1", &dest));
960+
961+ struct connection *const conn = setup_connection();
962+ struct esp_tuple *const esp_tuple = setup_esp_tuple(0xAABBCCDD, &dest, conn);
963+
964+ esp_speedup = 1;
965+ hip_userspace_ipsec = 0;
966+ conn->original.hook = NF_IP_FORWARD;
967+
968+ fail_if(hip_fw_manage_esp_rule(esp_tuple, &dest, true) != 0);
969+ fail_if(strcmp(mock_system_last, expected) != 0, "Unexpected rule was generated");
970+}
971+END_TEST
972+
973+START_TEST(test_hip_fw_manage_esp_rule_inet4_udp)
974+{
975+ mock_system = true;
976+
977+ static const char *const expected = "iptables -I HIPFW-OUTPUT -p UDP "
978+ "--dport 10500 --sport 10500 -d 192.168.1.1 "
979+ "-m u32 --u32 '4&0x1FFF=0 && 0>>22&0x3C@8=0xAABBCCDD' "
980+ "-j ACCEPT";
981+
982+ struct in6_addr dest;
983+ assert(inet_pton(AF_INET6, "::ffff:192.168.1.1", &dest));
984+
985+ struct connection *const conn = setup_connection();
986+ struct esp_tuple *const esp_tuple = setup_esp_tuple(0xAABBCCDD, &dest, conn);
987+
988+ esp_speedup = 1;
989+ hip_userspace_ipsec = 0;
990+ conn->original.hook = NF_IP_LOCAL_OUT;
991+ conn->udp_encap = true;
992+
993+ fail_if(hip_fw_manage_esp_rule(esp_tuple, &dest, true) != 0,
994+ "Adding an iptables rule failed");
995+ fail_if(!mock_system_last, "No iptables command was executed");
996+ fail_if(strcmp(mock_system_last, expected) != 0, "Unexpected rule was generated");
997+}
998+END_TEST
999+
1000 Suite *firewall_conntrack(void)
1001 {
1002 Suite *s = suite_create("firewall/conntrack");
1003
1004 TCase *tc_conntrack = tcase_create("Conntrack");
1005 tcase_add_test(tc_conntrack, test_hip_fw_conntrack_periodic_cleanup_timeout);
1006+ tcase_add_test(tc_conntrack, test_parse_iptables_esp_rule);
1007 tcase_add_test(tc_conntrack, test_hip_fw_conntrack_periodic_cleanup_glitched_system_time);
1008 tcase_add_test(tc_conntrack, test_hip_fw_conntrack_periodic_cleanup_glitched_packet_time);
1009+ tcase_add_test(tc_conntrack, test_hip_fw_manage_esp_rule_not_enabled);
1010+ tcase_add_test(tc_conntrack, test_hip_fw_manage_esp_rule_needs_userspace);
1011+ tcase_add_test(tc_conntrack, test_hip_fw_manage_esp_rule_inet6);
1012+ tcase_add_test(tc_conntrack, test_hip_fw_manage_esp_rule_inet4);
1013+ tcase_add_test(tc_conntrack, test_hip_fw_manage_esp_rule_inet4_udp);
1014 suite_add_tcase(s, tc_conntrack);
1015
1016 return s;
1017
1018=== added file 'test/firewall/helpers.c'
1019--- test/firewall/helpers.c 1970-01-01 00:00:00 +0000
1020+++ test/firewall/helpers.c 2011-04-04 22:33:36 +0000
1021@@ -0,0 +1,59 @@
1022+/*
1023+ * Copyright (c) 2011 Aalto University and RWTH Aachen University.
1024+ *
1025+ * Permission is hereby granted, free of charge, to any person
1026+ * obtaining a copy of this software and associated documentation
1027+ * files (the "Software"), to deal in the Software without
1028+ * restriction, including without limitation the rights to use,
1029+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
1030+ * copies of the Software, and to permit persons to whom the
1031+ * Software is furnished to do so, subject to the following
1032+ * conditions:
1033+ *
1034+ * The above copyright notice and this permission notice shall be
1035+ * included in all copies or substantial portions of the Software.
1036+ *
1037+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
1038+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
1039+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
1040+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
1041+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
1042+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
1043+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
1044+ * OTHER DEALINGS IN THE SOFTWARE.
1045+ */
1046+
1047+#define _BSD_SOURCE
1048+
1049+#include <check.h>
1050+#include <stdlib.h>
1051+
1052+#include "lib/core/common.h"
1053+#include "firewall/helpers.h"
1054+#include "test/mocks.h"
1055+#include "test_suites.h"
1056+
1057+
1058+START_TEST(test_system_printf)
1059+{
1060+ mock_system = true;
1061+
1062+ char str[MAX_COMMAND_LINE + 1];
1063+ memset(str, '.', sizeof(str));
1064+ str[MAX_COMMAND_LINE] = '\0';
1065+
1066+ fail_unless(system_printf("_%s", str) == -1, "Truncated command line executed");
1067+ fail_unless(system_printf("%s", str) == 0, "Fitting command line not executed");
1068+}
1069+END_TEST
1070+
1071+Suite *firewall_helpers(void)
1072+{
1073+ Suite *s = suite_create("firewall/helpers");
1074+
1075+ TCase *tc_helpers = tcase_create("helpers");
1076+ tcase_add_test(tc_helpers, test_system_printf);
1077+ suite_add_tcase(s, tc_helpers);
1078+
1079+ return s;
1080+}
1081
1082=== modified file 'test/firewall/test_suites.h'
1083--- test/firewall/test_suites.h 2011-04-04 22:33:36 +0000
1084+++ test/firewall/test_suites.h 2011-04-04 22:33:36 +0000
1085@@ -30,6 +30,7 @@
1086
1087 Suite *firewall_conntrack(void);
1088 Suite *firewall_file_buffer(void);
1089+Suite *firewall_helpers(void);
1090 Suite *firewall_line_parser(void);
1091 Suite *firewall_port_bindings(void);
1092

Subscribers

People subscribed via source and target branches

to all changes: