Merge lp:~cameronnemo/upstart/ipv6 into lp:upstart

Proposed by Cameron Norman
Status: Merged
Merged at revision: 1594
Proposed branch: lp:~cameronnemo/upstart/ipv6
Merge into: lp:upstart
Diff against target: 202 lines (+92/-25)
3 files modified
ChangeLog (+16/-0)
extra/man/socket-event.7 (+10/-1)
extra/upstart-socket-bridge.c (+66/-24)
To merge this branch: bzr merge lp:~cameronnemo/upstart/ipv6
Reviewer Review Type Date Requested Status
James Hunt Approve
Review via email: mp+204599@code.launchpad.net

Description of the change

Adds the inet6 option for the socket event parameter PROTO and the corresponding man page examples.

To post a comment you must log in.
lp:~cameronnemo/upstart/ipv6 updated
1595. By Cameron Norman

remerge from trunk

1596. By Cameron Norman

whoopsie in ChangeLog

Revision history for this message
James Hunt (jamesodhunt) wrote :

Hi Cameron,

Thanks to you and Kai for undertaking this new feature!

A few comments:

* extra/upstart-socket-bridge.c:
  - epoll_watcher():
    - 'break' has been lost from end of the AF_INET block.
    - buffer should not use nih_local (as that is only for allocated storage) and should really be at the
      top of the 'for' block.
    - AF_INET6 block missing a 'break' at the end.
  - job_add_socket(): '! strcmp (*env, "ADDR")' needs to be updated for IPv6 (using inet_pton() rather than
    inet_aton() maybe?).

I tried testing this using the new IPv6 example from socket-event.7, but I think the last comment above is causing the job to be rejected.

review: Needs Fixing
lp:~cameronnemo/upstart/ipv6 updated
1597. By Cameron Norman

upstart-socket-bridge.c: epoll_watcher() and job_add_socket() fixes for ipv6

1598. By Cameron Norman

upstart-socket-bridge.c: job_add_socket(): pton returns 1 on success, not 0

Revision history for this message
James Hunt (jamesodhunt) wrote :

Hi Cameron,

Thanks for updating. There is still a problem with inet_pton(): the test should error if the value returned != 1 since currently all valid IPv6 addresses are rejected. Please could you fix that up and test.

I'm testing this with a job that specifies:

start on socket PROTO=inet6 PORT=80 ADDR=::1
script
echo ok
set
end script

Then running the event monitor:

$ upstart-monitor &

Then, running the bridge in debug mode in the foreground:

$ sudo ./upstart-socket-bridge --debug

And finally connecting to port 80 via IPv6:

$ echo foo | socat 'tcp6-connect:[::1]:80' -

Thanks!

review: Needs Fixing
lp:~cameronnemo/upstart/ipv6 updated
1599. By Cameron Norman

fixed ptons usage

Revision history for this message
Cameron Norman (cameronnemo) wrote :

Thanks for the help and patience James. I am going to try to work on this tonight and tommorow, and will comment here if I reach success, or if I need any help (if that is alright).

Revision history for this message
James Hunt (jamesodhunt) wrote :

Hi Cameron - no problem. Feel free to join #upstart on freenode if you have questions.

Revision history for this message
Cameron Norman (cameronnemo) wrote :

So I tested it on a Saucy machine and it seems to be working. Would you like to re-review it?

Revision history for this message
James Hunt (jamesodhunt) wrote :

Hi Cameron,

Thanks for updating. I've found another issue though: if there are 2 jobs that specify 'start on socket', one for IPv4 the other for IPv6, the bridge will be unable to call bind(2) for each protocol version currently:

$ cat <<EOT | sudo tee /etc/init/test-socket-bridge-ipv4.confj
start on socket PROTO=inet PORT=80 ADDR=127.0.0.1
exec echo ipv4
EOT

$ cat <<EOT | sudo tee /etc/init/test-socket-bridge-ipv6.conf
start on socket PROTO=inet6 PORT=80 ADDR=::1
exec echo ipv6
EOT

$ sudo extra/upstart-socket-bridge --debug
job_add_socket: Found socket
upstart_job_added: Job got added /com/ubuntu/Upstart/jobs/test_2dsocket_2dbridge_2dipv6
job_add_socket: Found socket
upstart-socket-bridge: Failed to bind socket in /com/ubuntu/Upstart/jobs/test_2dsocket_2dbridge_2dipv4: Address already in use

I think we may need some IPV6_V6ONLY setsockopt(2) magic to resolve this?

review: Needs Fixing
Revision history for this message
Cameron Norman (cameronnemo) wrote :

I have a question about that. Should we not instead do IPv4 mapped IPv6?

Revision history for this message
Cameron Norman (cameronnemo) wrote :

But you would still have to use IPV6_V6ONLY in that scenario. And even then with IPV4 mapped IPv6 you would need to give the job two fd's, which we cannot do with the current socket activation protocol (correct?).

lp:~cameronnemo/upstart/ipv6 updated
1600. By Cameron Norman

use setsockopt to only bind to ipv6

1601. By Cameron Norman

whoops

1602. By Cameron Norman

put the setsockopt /after/ the sockfd is created

Revision history for this message
Cameron Norman (cameronnemo) wrote :

Ok, so I got it working and tested it with the following job:

start on socket PROTO=inet6 PORT=80 ADDR=::1

exec socat fd:"$UPSTART_FDS" -

Revision history for this message
James Hunt (jamesodhunt) wrote :

Hi Cameron,

Yes, this does seem to be working, but if you run 'sudo upstart-socket-bridge --debug' with the 2 test jobs above, an error is raised on the 2nd bind:

$ sudo extra/upstart-socket-bridge --debug
job_add_socket: Found socket
upstart_job_added: Job got added /com/ubuntu/Upstart/jobs/test_2dsocket_2dbridge_2dipv6
job_add_socket: Found socket
upstart-socket-bridge: Failed to bind socket in /com/ubuntu/Upstart/jobs/test_2dsocket_2dbridge_2dipv4: Address already in use

Revision history for this message
Cameron Norman (cameronnemo) wrote :

That is strange, because I am not seeing the failure. Are you sure you are using r1602?

Revision history for this message
Cameron Norman (cameronnemo) wrote :

Wait, I think I may understand why. Is the system upstart-socket-bridge creating the socket for test-socket-bridge-ipv4.conf at boot? That would explain why the ipv6 one is not binding: the socket bridge you run at boot can not bind to the ipv6 socket. Other than that, I am at a loss as to why it works here, but not there.

Revision history for this message
Cameron Norman (cameronnemo) wrote :

why the ipv6 one is not binding
                    ^^^

I meant that it is binding, sorry.

Revision history for this message
James Hunt (jamesodhunt) wrote :

Hi Cameron - yes, you were right :-)

Merged - thanks again for your and Kai's work on this!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'ChangeLog'
--- ChangeLog 2014-02-04 11:32:56 +0000
+++ ChangeLog 2014-02-11 02:35:11 +0000
@@ -1,3 +1,10 @@
12014-02-04 Cameron Norman <camerontnorman@gmail.com>
2
3 * extra/upstart-socket-bridge.c:
4 - epoll_watcher(): Fix missing breaks in a switch, move buffer and
5 remove its nih_local usage.
6 - job_add_socket(): added case for name_len being a AF_INET6 address.
7
12014-02-04 James Hunt <james.hunt@ubuntu.com>82014-02-04 James Hunt <james.hunt@ubuntu.com>
29
3 * extra/man/socket-event.7: Environment variable is10 * extra/man/socket-event.7: Environment variable is
@@ -9,6 +16,15 @@
9 stealing the console in an container: fopen(3) may not specify16 stealing the console in an container: fopen(3) may not specify
10 O_NOCTTY and Upstart should not own the console (LP: #1263738).17 O_NOCTTY and Upstart should not own the console (LP: #1263738).
1118
192014-01-20 Cameron Norman <camerontnorman@gmail.com>
20
21 * extra/upstart-socket-bridge.c: Fixed indentation, used nih_local.
22 * extra/man/socket-event.7: Added inet6 example.
23
242014-01-19 Kai Mast <mail@kai-mast.de>
25
26 * extra/upstart-socket-bridge.c: Added IPv6 support (LP: #942955).
27
122014-01-17 James Hunt <james.hunt@ubuntu.com>282014-01-17 James Hunt <james.hunt@ubuntu.com>
1329
14 * init/conf.c:30 * init/conf.c:
1531
=== modified file 'extra/man/socket-event.7'
--- extra/man/socket-event.7 2014-02-04 11:32:56 +0000
+++ extra/man/socket-event.7 2014-02-11 02:35:11 +0000
@@ -39,7 +39,7 @@
39.\"39.\"
40.SH EXAMPLES40.SH EXAMPLES
41.\"41.\"
42.SS Internet socket42.SS Internet (IPv4) socket
43Start web server when first client connects from localhost:43Start web server when first client connects from localhost:
44.RS44.RS
45.nf45.nf
@@ -48,6 +48,15 @@
48.fi48.fi
49.RE49.RE
50.\"50.\"
51.SS Internet (IPv6) socket
52Start job when a client connects from localhost:
53.RS
54.nf
55
56start on socket PROTO=inet6 PORT=80 ADDR=::1
57.fi
58.RE
59.\"
51.SS Local socket60.SS Local socket
52.P61.P
53.RS62.RS
5463
=== modified file 'extra/upstart-socket-bridge.c'
--- extra/upstart-socket-bridge.c 2013-10-23 16:33:09 +0000
+++ extra/upstart-socket-bridge.c 2014-02-11 02:35:11 +0000
@@ -67,9 +67,10 @@
67 NihList entry;67 NihList entry;
6868
69 union {69 union {
70 struct sockaddr addr;70 struct sockaddr addr;
71 struct sockaddr_in sin_addr;71 struct sockaddr_in sin_addr;
72 struct sockaddr_un sun_addr;72 struct sockaddr_in6 sin6_addr;
73 struct sockaddr_un sun_addr;
73 };74 };
74 socklen_t addrlen;75 socklen_t addrlen;
7576
@@ -295,6 +296,7 @@
295 size_t env_len = 0;296 size_t env_len = 0;
296 char *var;297 char *var;
297 DBusPendingCall *pending_call;298 DBusPendingCall *pending_call;
299 char buffer[INET6_ADDRSTRLEN];
298300
299 if (event[i].events & EPOLLIN)301 if (event[i].events & EPOLLIN)
300 nih_debug ("%p EPOLLIN", sock);302 nih_debug ("%p EPOLLIN", sock);
@@ -308,18 +310,35 @@
308 switch (sock->addr.sa_family) {310 switch (sock->addr.sa_family) {
309 case AF_INET:311 case AF_INET:
310 NIH_MUST (nih_str_array_add (&env, NULL, &env_len,312 NIH_MUST (nih_str_array_add (&env, NULL, &env_len,
311 "PROTO=inet"));313 "PROTO=inet"));
312314
313 var = NIH_MUST (nih_sprintf (NULL, "PORT=%d",315 var = NIH_MUST (nih_sprintf (NULL, "PORT=%d",
314 ntohs (sock->sin_addr.sin_port)));316 ntohs (sock->sin_addr.sin_port)));
315 NIH_MUST (nih_str_array_addp (&env, NULL, &env_len,317 NIH_MUST (nih_str_array_addp (&env, NULL, &env_len,
316 var));318 var));
317 nih_discard (var);319 nih_discard (var);
318320
319 var = NIH_MUST (nih_sprintf (NULL, "ADDR=%s",321 var = NIH_MUST (nih_sprintf (NULL, "ADDR=%s",
320 inet_ntoa (sock->sin_addr.sin_addr)));322 inet_ntoa (sock->sin_addr.sin_addr)));
321 NIH_MUST (nih_str_array_addp (&env, NULL, &env_len,323 NIH_MUST (nih_str_array_addp (&env, NULL, &env_len,
322 var));324 var));
325 nih_discard (var);
326 break;
327 case AF_INET6:
328 NIH_MUST (nih_str_array_add (&env, NULL, &env_len,
329 "PROTO=inet6"));
330
331 var = NIH_MUST (nih_sprintf (NULL, "PORT=%d",
332 ntohs (sock->sin6_addr.sin6_port)));
333 NIH_MUST (nih_str_array_addp (&env, NULL, &env_len,
334 var));
335 nih_discard (var);
336
337 var = NIH_MUST (nih_sprintf (NULL, "ADDR=%s",
338 inet_ntop(AF_INET6, &sock->sin6_addr.sin6_addr, buffer, INET6_ADDRSTRLEN)));
339
340 NIH_MUST (nih_str_array_addp (&env, NULL, &env_len,
341 var));
323 nih_discard (var);342 nih_discard (var);
324 break;343 break;
325 case AF_UNIX:344 case AF_UNIX:
@@ -504,6 +523,11 @@
504 sock->sin_addr.sin_family = AF_INET;523 sock->sin_addr.sin_family = AF_INET;
505 sock->sin_addr.sin_addr.s_addr = INADDR_ANY;524 sock->sin_addr.sin_addr.s_addr = INADDR_ANY;
506 components = 1;525 components = 1;
526 } else if (! strcmp (val, "inet6")) {
527 sock->addrlen = sizeof sock->sin6_addr;
528 sock->sin6_addr.sin6_family = AF_INET6;
529 sock->sin6_addr.sin6_addr = in6addr_any;
530 components = 1;
507 } else if (! strcmp (val, "unix")) {531 } else if (! strcmp (val, "unix")) {
508 sock->addrlen = sizeof sock->sun_addr;532 sock->addrlen = sizeof sock->sun_addr;
509 sock->sun_addr.sun_family = AF_UNIX;533 sock->sun_addr.sun_family = AF_UNIX;
@@ -515,22 +539,31 @@
515 }539 }
516540
517 } else if (! strncmp (*env, "PORT", name_len)541 } else if (! strncmp (*env, "PORT", name_len)
518 && (sock->sin_addr.sin_family == AF_INET)) {542 && (sock->sin_addr.sin_family == AF_INET)) {
519 sock->sin_addr.sin_port = htons (atoi (val));543 sock->sin_addr.sin_port = htons (atoi (val));
520 components--;544 components--;
521545 } else if (! strncmp (*env, "PORT", name_len)
546 && (sock->sin6_addr.sin6_family == AF_INET6)) {
547 sock->sin6_addr.sin6_port = htons (atoi (val));
548 components--;
522 } else if (! strncmp (*env, "ADDR", name_len)549 } else if (! strncmp (*env, "ADDR", name_len)
523 && (sock->sin_addr.sin_family == AF_INET)) {550 && (sock->sin_addr.sin_family == AF_INET)) {
524 if (inet_aton (val, &(sock->sin_addr.sin_addr)) == 0) {551 if (inet_aton (val, &(sock->sin_addr.sin_addr)) == 0) {
525 nih_warn ("Ignored socket event with invalid ADDR=%s in %s",552 nih_warn ("Ignored socket event with invalid ADDR=%s in %s",
526 val, job->path);553 val, job->path);
527 goto error;554 goto error;
528 }555 }
529556 } else if (! strncmp (*env, "ADDR", name_len)
557 && (sock->sin6_addr.sin6_family == AF_INET6)) {
558 if (inet_pton (AF_INET6, val, &(sock->sin6_addr.sin6_addr)) != 1) {
559 nih_warn ("Ignored socket event with invalid ADDR=%s in %s",
560 val, job->path);
561 goto error;
562 }
530 } else if (! strncmp (*env, "PATH", name_len)563 } else if (! strncmp (*env, "PATH", name_len)
531 && (sock->sun_addr.sun_family == AF_UNIX)) {564 && (sock->sun_addr.sun_family == AF_UNIX)) {
532 strncpy (sock->sun_addr.sun_path, val,565 strncpy (sock->sun_addr.sun_path, val,
533 sizeof sock->sun_addr.sun_path);566 sizeof sock->sun_addr.sun_path);
534567
535 if (sock->sun_addr.sun_path[0] == '@')568 if (sock->sun_addr.sun_path[0] == '@')
536 sock->sun_addr.sun_path[0] = '\0';569 sock->sun_addr.sun_path[0] = '\0';
@@ -567,6 +600,15 @@
567 goto error;600 goto error;
568 }601 }
569602
603 /* If socket is ipv6, need to set IPV6_V6ONLY option */
604 if (sock->sin6_addr.sin6_family == AF_INET6 &&
605 setsockopt (sock->sock, SOL_IPV6, IPV6_V6ONLY,
606 &opt, sizeof opt) < 0) {
607 nih_warn ("Failed to set IPV6 only option in %s: %s",
608 job->path, strerror (errno));
609 goto error;
610 }
611
570 if (bind (sock->sock, &sock->addr, sock->addrlen) < 0) {612 if (bind (sock->sock, &sock->addr, sock->addrlen) < 0) {
571 nih_warn ("Failed to bind socket in %s: %s",613 nih_warn ("Failed to bind socket in %s: %s",
572 job->path, strerror (errno));614 job->path, strerror (errno));

Subscribers

People subscribed via source and target branches