Is IP address on the same subnet as the local machine (with IPv6 support)
Asked Answered
M

4

5

Does anyone have some code that will determine if an IP address (IPv4 or IPv6) is on the same subnet as the machine running the application? I've seen numerous examples of code that does this with IPv4 but I can't find any that support IPv6.

EDIT:

I'm unsure if I'm understanding all the differences between v4 and v6 so here's a bit more to my question. I have an application that serves both internet clients and intranet clients, that is to say there are clients that are on the same physical network as the server. So sometimes there are routers between the client and sometimes there aren't. With IPv4 I can determine this by checking the client IP address against the server IP address and the subnet so if my server's IP and subnet mask are respectively:

192.168.123.15 255.255.255.0

And the server receives a client request from 192.168.123.100 I know that there is no router standing between the client and server. However, if the server receives a client request from 192.168.1.100 or perhaps 67.7.23.4 I know that there is a router standing between those clients and the server. In .Net I can gather the client and server IP addresses (both v4 and v6) but I cannot find the IPv6 subnet mask.

Is there a way to gather this info in .Net or is there some difference between IPv4 and IPv6 that I'm misunderstanding?

EDIT x2:

I posted this on the MS connect site to see if it's something they're working on or if there's a reason they haven't added an IPv6Mask property to the UnicastIPAddressInformation class.

https://connect.microsoft.com/VisualStudio/feedback/details/643031/unicastipaddressinformation-class-has-no-ipv6mask-property

I also posted the same question on the MSDN forums around the same time. 1800+ views and not a single reply. Guess I'm not the only one who's curious about this.

http://social.msdn.microsoft.com/Forums/en-US/netfxnetcom/thread/dd30e161-9be5-4d70-97c0-22e2756ce953

Monocoque answered 9/9, 2010 at 18:53 Comment(4)
How is the IPv6 address offered? Is there a transition mechanisms being used or is it DHCPv6?Precedent
Well I'm trying to make it function under any number of circumstances so let's say both?Monocoque
maybe this should be really asked from serverfault.comFootworn
No, it's a question on how to programmatically determine if a client and server are on the same subnet.Monocoque
T
3

It doesn't look like the framework has a way to do this. The most accurate way would be to do a route lookup, but I don't see a good way to do that in C#. (Under Linux, I'd do /sbin/ip -6 route get <ipv6-addr> and see which route gets returned.) You'd have to find a native call to do this in Windows; I don't see a command line application.

The best way might be to parse the output of netsh interface ipv6 show route verbose. You could look for any non-/128 prefixes and do a longest prefix match on those. (well, if you hit a /128, that's an address assigned to the box)

You could also check the neighbor table. (netsh interface ipv6 show neighbors), but that might not contain the entry you're looking for if you haven't talked to that host recently.

Other potential issues you'll need to consider:

  • Link-local addresses (fe80::/10) (and multicast, loopback, and unspecified - everything in the table)
  • The fact that in IPv6, an assigned address does not imply an on-link prefix. The prefix table is separate. It's not clear how to test for this under Windows, but netsh interface ipv6 show siteprefixes might help. It looks like Windows might actually treat this more like IPv4 than the standard expects.

Edit: It sounds like checking the neighbor table will be the path of least resistance for you to do this; if you're accepting a connection from the intranet and then turning around and checking the neighbor table, you can be reasonably assured that if the neighbor is local, it will exist in the table. If you check the neighbor table, BE CAREFUL to only look at the neighbor table for LAN interfaces. (The ISATAP interface, installed by default on many Windows system, exposes the entire IPv4 internet as a link-local "subnet".)

Again, IPv6 addresses have no concept of a "netmask", since the on-link prefix table is separate from address assignment. However, if you have a server sitting somewhere you can be probably 99% certain that it is on a /64. (though you'd have to be careful; if it itself was a tunnel endpoint, sometimes I've seen longer prefixes assigned for 6in4 tunnels) So a quick and dirty algorithm would be:

  • Ignore all addresses where the first 64 bits are 0 (local loopback)
  • Ignore all addresses matching ff00::/8 (multicast)
  • If the address matches fe80::/10, it is interface local. Be careful with this, because if you have an ISATAP interface enabled, "link-local" means "automatic tunnel to the entire IPv4 internet"! (not good.) So it might be best to not trust link-local addresses unless you are certain they are coming from a LAN interface. (they can't be routed)
  • (Now the complicated part) Determine if the address is a neighbor. (neighbor lookup) The hack-and-slash solution is to check all the IPv6 addresses configured on the system (autoconfigured via stateless address autoconfiguration, DHCPv6, or static, if you can determine it) and check the first 64 bits. On a server (assuming no funky tunnels are configured with non-/64 prefixes) this will be a bug in rare cases, since - again - you can't be sure if the address is really on-link unless you check the on-link prefix table. (which Windows doesn't have a concept of; it seems to be stored in the route table.) Most network devices that can be configured to send out router advertisements on an Ethernet interface will always advertise a /64 prefix. If you can check that the packet came in on a LAN interface, it will be even less likely that this will be a bug.

Edit 2

I have written some code to parse the IPv6 route table and posted it here. It doesn't solve the hard problems posed in this question yet, but it's a step in the right direction.

Tishtisha answered 11/2, 2011 at 4:3 Comment(5)
There has to be a more native way of doing this than spawning an app and looking at its output.Pironi
@Steven, if you want to use P/Invoke and figure out the calls, that would work - or find someone who has written a Windows .NET library that does the same. I would argue that spawning an app with well-known output would possibly be less error-prone than making native calls (hopefully netsh would be updated in different Windows versions if the native calls change - spawning an app insulates you from that potential turmoil)Tishtisha
You may well be right, as a practical matter, in saying a command-line interface is quicker to implement and more likely to survive changes. Somehow, I can't endorse it, though.Pironi
Well I don't think this is quite an answer but it's looking like there simply isn't one yet so I'm going to award you the bounty because I appreciate the amount of work you put into this.Monocoque
@Spencer, thanks, I was thinking of writing some C# code for this problem but hadn't gotten around to it - too much work to do at the day job ;-). Will update my answer if I do.Tishtisha
R
3

The moral equivalent of the IPv4 network mask in IPv6 is called the prefix length. (Actually, we like to talk about prefix length instead of network mask in IPv4 too, but some people haven't gotten the memo yet.)

An additional wrinkle in IPv6 that doesn't come up with IPv4 is that default routers advertise their presence on the link by answering router solicitation queries from hosts. Hosts keep a list of all the default routers they find this way along with their valid and preferred lifetimes. Routers may also advertise zero, one or more prefixes for which they serve as a default router, and hosts keep a list of these along with their associated routers and their individual valid and preferred lifetimes.

Each prefix has two ancillary bits in the advertisement, A and L, which get coalesced by hosts when they're added to the prefix list. The A=1 bit indicates whether hosts are allowed to automatically self-configure an interface address with that prefix, whereas A=0 means that hosts are required to obtain an address with that prefix via DHCPv6 or manually. The L=1 bit indicates that the prefix is "on link" and the host may use neighbor discovery (the IPv6 equivalent of ARP) to send directly across the network, whereas L=0 indicates that the prefix is "off link" and that hosts are required to send all traffic for that prefix to a default router.

Long story short: if you want to know that an IPv6 address is "on link" then you have to walk the IPv6 prefix list and compare each one with the address and also look at the L-bit to make sure it's an on-link prefix. Alas, I only know the BSD-system way of looking at the prefix list, i.e. sysctl(ICMPV6CTL_ND6_PRLIST, ...). Really not sure what if anything MSFT has made available to C# developers for that.

Yes, I realize this isn't a complete answer. Alas.

Righthander answered 11/2, 2011 at 23:33 Comment(3)
Yes, the more info the better. +1 So, I believe the way a client on an IPv4 network wishing to establish a connection to another computer would determine if it needed to send the packet to the default gateway would be to compare the destination IP address, the clients own netmask and the clients own IP address. If the mask applied to both IP addresses didn't match the packet would be sent to the gateway. Can you explain a bit more how a client would do this in IPv6 and what information it uses?Monocoque
IPv4 doesn't have the notion of "off-link" vs. "on-link" so, if a destination address shares the same prefix with an interface address, then the assumption is it's on the same link, and the host is required to ARP for the destination link-layer address. That's why what you're doing in IPv4 is sufficient.Righthander
With IPv6, the host doesn't look at the interface addresses when deciding whether to send neighbor solicitation for the link-layer address. Instead, the host consults the prefix list, which is constructed from the results of receiving router advertisements in response to router solicitations (and also by manual static configuration).Righthander
D
2

I've been looking in the wmi documentation trying to find the ipv6 prefix and I'm also struggling to find it. I have working code for IPv4, however.

I think you could safely assume the prefix is /64 for that network segment. If you do that, you can just compare the first 8 bytes of each address. More often than not an individual local networks running applications will be a /64, even point to point links are often given a complete /64 range even though only 2 are used.

Doze answered 14/2, 2011 at 12:33 Comment(2)
+1, RFC 4291 requires 64 bit interface identifiers, though RFC 4862 leaves open the possibility of non-/64 prefix lengths. This is the simplest solution (with the caveats I mentioned in my answer)Tishtisha
Yes, also address distribution via Router Advertisement only works with /64 prefixes, due to the 64bit interface identifier requirement. You can use different ones statically or with DHCPv6, but I've never seen anything other than a /64 being used.Doze
C
1

In 99% of the cases, this is simple. All subnets in IPv6 are /64 prefixes therefore if the leftmost 64 bits of a prefix are identical, they are in the same subnet.

Now it is true that some people are doing wierd things and making subnets with longer prefixes but the majority of this is dealing with point-to-point circuits where they use a /126 to number the two endpoints and then RESERVE the /64 containing the /126. An application would never run into this situation since no servers are involved.

There is no good reason to support subnet sizes other than /64.

Chloras answered 16/2, 2011 at 22:21 Comment(1)
I don't know that I can agree with "There is no good reason to support subnet sizes other than /64." Violating specs is how we end up with Internet Explorer.Monocoque

© 2022 - 2024 — McMap. All rights reserved.