Bug 206012 - jail(8): Cannot assign link-local IPv6 address to a jail
Summary: jail(8): Cannot assign link-local IPv6 address to a jail
Status: Open
Alias: None
Product: Base System
Classification: Unclassified
Component: kern (show other bugs)
Version: CURRENT
Hardware: Any Any
: --- Affects Only Me
Assignee: Jamie Gritton
Depends on:
Reported: 2016-01-07 19:27 UTC by Mark Felder
Modified: 2023-04-28 16:30 UTC (History)
13 users (show)

See Also:


Note You need to log in before you can comment on or make changes to this bug.
Description Mark Felder freebsd_committer 2016-01-07 19:27:37 UTC
When attempting to assign a link-local IP to a jail one of the following things happens:

* it either does not work at all and the jail refuses to start for some reason claiming the jail does not exist

* Jail starts, but ifconfig in the jail shows there is no IP on the interfaces. Trying to bind to it with nc(1) to test fails.

* Adding the index to the IPv6 address causes the jail to reject the address as invalid and not start:

jail: v6test: ip6.addr: not an IPv6 address: fe80::216:3eff:fe72:eadd%xn0
Comment 1 Jamie Gritton freebsd_committer 2016-01-18 04:07:39 UTC
I'm not surprised such a thing wouldn't work.  The IPv6 jail-related kernel code knows only IP addresses, and doesn't touch scope.  These scoped link-local addresses are a little beyond me I must admit*, but I can see at least one essential jail concept breaking down in that paradigm: any IP communication of the jail with itself is forcibly rerouted to localhost - which in the IPv6 case kind of assumes the global scope where ::1 lives.  I'm sure there are many other ways that are a good less simple that the whole scope concept just doesn't make it into the part of the kernel that knows/cares about jails.

I imagine it would take a lot of support deep in the IPv6 code to make jails work on link-local addresses, and frankly I just don't see than happening.  And I wonder if it would be a good idea anyway - shunting a link-local address off to a jail sounds it like could break things that depend on such addresses existing in a regular non-jailed way.

(* It's also beyond me why a committee of people who know networking much better than I do ever though such an abomination as this out-of-band special "scope" address should even exist, but I digress).
Comment 2 Sascha Biberhofer 2016-05-14 13:20:06 UTC
Just to add to this:

It _is_ possible to assign a link-local IPv6 address to a jail. But you have to use the interface's scope identifier to do so, i.e. instead of writing something like

ip6.addr =  "lo1|fe80::dead:beef/64";

you need to use 

ip6.addr =  "lo1|fe80:X::dead:beef/64";

where X is the scope-id of your interface. This number does depend - as far as I can tell - on the order in which the kernel creates the interfaces, so you either have to take a wild guess or take a look at the ouput of ifconfig, which prints the scopeid of any IPv6-address assigned to it:

inet6 fe80::dead:beef%lo1 prefixlen 64 scopeid 0x5

It would be really nice if this hack wasn't necessary, i.e. if the system would do this either by itself, or if there was a reliable way to get the scope-id of an interface without having to guess. As it stands right now, using scope-ids to assign link-local addresses works, but might break on reboot if a new interface is added.
Comment 3 Jamie Gritton freebsd_committer 2016-05-28 22:55:05 UTC
It wouldn't be too hard to automate the transition between fe80:ifnum::dead:beef and fe80::dead:beef%ifname.  Jail uses inet_pton and inet_ntop which aren't aware of this, but it looks like it could use getaddrinfo and getameinfo which use the scope ID, or if_nametoindex and if_indextoname which give me the number directly.

I would have to move this scope ID to/from the second and third octets in the inet6 address, which is rather non-standard but I assume a long-standing FreeBSD way of doing things.
Comment 4 Sascha Biberhofer 2016-06-04 14:30:29 UTC
Hi Jamie,

That would be a great and at least avoid the issue of having to specify the (slightly volatile) interface number explicitly in jail.conf.

However, since the interface that an address is meant for is always specified in jail.conf anyway (either via the interface parameter or the if|address format in ip4.addr and ip6.addr) it would be even nicer if we could generate the interface index directly from the interface information in jail.conf, i.e.

ip6.addr = 'lo1|fe80::dead:beef' 


interface = 'lo1'
ip6.addr = 'fe80::dead:beef'

should suffice for the sake of convenience. I guess one could map these forms to fe80::dead:beef%if and then map this to the index form like you suggested.
Comment 5 Dan Langille freebsd_committer 2020-10-26 01:14:06 UTC
4 years later, I'm hoping to do something similar...
Comment 6 Siva Mahadevan 2023-04-07 15:39:11 UTC
What's the current status on this? I'm trying to setup jails with the following syntax and it gave me errors of the form "ip6.addr: not an IPv6 address: fe80::2%re0" where re0 is the interface name of my ethernet link.

in /etc/jail.conf:

jailname {
    ip6.addr = "re0|fe80::1%re0/64";

I had to resort to doing `ip6.addr = "re0|fe80:<volatile scope numeric id>::1/64`; to make it work.
Comment 7 Jamie Gritton freebsd_committer 2023-04-13 17:03:38 UTC
I suppose it's time to look into actually getting this done.
Comment 8 Zhenlei Huang freebsd_committer 2023-04-14 02:15:10 UTC
(In reply to Jamie Gritton from comment #7)
CC @melifaro.

IIRC @melifaro has plan to remove IPv6 embedded form.

If that is done I guess this can be also addressed, for current/14.
Comment 9 Jamie Gritton freebsd_committer 2023-04-24 04:53:52 UTC
It seems like the scope identifier is implied: If I add any address in the fe80:: range, it automatically gains the scope ID of the interface it was added to.  So now I wonder: does jail(8) even need to support the link-local address with the interface/scope tacked into the end?  It seems to work fine if I just use a bare fe80:: address.  And there's nothing magic in the address, no embedded scope ID or anything.
Comment 10 Zhenlei Huang freebsd_committer 2023-04-25 01:07:14 UTC
> IIRC @melifaro has plan to remove IPv6 embedded form.
> If that is done I guess this can be also addressed, for current/14.
I was wrong.

To be clear, a link-local IP without scope(which interface) is not useful. Actually it is perfect valid for two interfaces have the **same** link-local IP, so application may want to distinguish them.

The kernel use embedded form for link-local IPv6 addresses, but the embedded scope should not be visible to userland, or userland hint kernel the embedded scope. So for userland applications we should support out-band means to indicate the scope.

A well known and standard form is fe80::dead:beef%em0, as required by RFC 4007.

For jail, I think the standard form should be supported, as most people would expect that form.

A jail specific form em0|fe80::dead:beef can also be supported.

It worth noting that for both forms, users should not get / set the embedded scope id. The embedded form is an implementation use in kernel side only.
Comment 11 Jamie Gritton freebsd_committer 2023-04-28 16:30:44 UTC
> It worth noting that for both forms, users should not get / set
> the embedded scope id. The embedded form is an implementation use
> in kernel side only.

Perhaps the end user shouldn't see it, and ideally it wouldn't be in user-space at all.

But doing it that way would require a change to the jail_set(2) ABI, adding the scope ID to the passed structure.  While I strove to make the interface extensible for adding new parameters, I didn't count on the format of an existing parameter changing.  So I don't want to break existing programs that pass a struct in6_addr by adding a scope to that.

Another choice is to have a second parameter for scoped IPv6 addresses.  Interestingly, there's not even an already-defined structure for that.  There's either in6_addr which doesn't have the scope, or sockaddr_in6, which has the scope but also the port, and flowinfo.  Still, it's a possibility, as definite a new structure of only the address and scope.

But what works best I think is to stick to the existing ABI, which would mean that user-space (libjail in particular) would be recognizing the embedded-scope hack.  I can't just pass the unscoped address and call it good, since as you mentioned that doesn't fully identify the address.  It's somewhat ugly, but at least transparent at the library level.