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

Proposed by Christof Mroz
Status: Superseded
Proposed branch: lp:~christof-mroz/hipl/hipfw-esp-speedup
Merge into: lp:hipl
Prerequisite: lp:~christof-mroz/hipl/hipfw-timeout
Diff against target: 1088 lines (+717/-22)
14 files modified
Makefile.am (+1/-0)
firewall/conntrack.c (+363/-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
Diego Biurrun Abstain
Stefan Götz (community) Needs Fixing
Review via email: mp+55153@code.launchpad.net

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

This proposal has been superseded by a proposal from 2011-04-04.

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 :
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 :

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 :

 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 :

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 :

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 :

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

5794. By Christof Mroz

Merged hipfw-timeout branch.

5795. By Christof Mroz

Clarify documentation of parse_iptables_esp_rule().

5796. By Christof Mroz

Atomically read and zero iptables packet counters.

5797. By Christof Mroz

Degrade comment about parser approach from a @todo to a @note.

The approach seems to work just fine, and it is unlikely to break in the near future.

5798. By Christof Mroz

Const correctness.

5799. By Christof Mroz

Merge duplicated code for enabling/disabling all ESP rules associated with one connection tuple.

5800. By Christof Mroz

Cosmetic: Whitespace and alignment cleanup.

5801. By Christof Mroz

Use correct chain name for outgoing packets.

5802. By Christof Mroz

Use true/false rather than 1/0 for boolean.

5803. By Christof Mroz

Unit tests for hip_fw_manage_esp_rule.

5804. By Christof Mroz

Fix unsigned <-> signed cast direction.

A signed value that's known to be positive may be safely cast into an unsigned
value, but cast unsigned to signed carries the risk of overflow (in C).

5805. By Christof Mroz

Export the maximum command line length as a global preprocessor #define.

5806. By Christof Mroz

Merged with hipfw-timeout branch.

5807. By Christof Mroz

Merged lp:~christof-mroz/hipl/hipfw-timeout rev 5810.

5808. By Christof Mroz

Fixed system_printf() length check.

5809. By Christof Mroz

Unit test for system_printf().

5810. By Christof Mroz

Declare tuple variable at tighter scope.

5811. By Christof Mroz

Document static global total_esp_rules_count.

Unmerged revisions

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

Subscribers

People subscribed via source and target branches

to all changes: