Rick Jones wrote:
> Spoon wrote:
>
>> Rick Jones wrote:
>>
>>> Probably. You want to make sure that you aren't overlapping with
>>> other traffic between the IP address pairs in question.
>>
>> I don't understand this sentence. Could you rephrase please?
>
> IP stacks have some latitude when it comes to picking the IP ID for a
> given datagram - some keep a global counter which increases for any IP
> datagram they send regardless of source/destination pair. Others may
> have an IP ID generator per route.
>
> So, if there is any other traffic, or traffic to/from the IP pair
> between which you are communicating, they can use some of the IP ID's
> you might be looking for and perhaps cause you to have a false
> positive on packet loss.
Thanks for pointing that out. (My OS is Linux 2.6.)
I've written a test program to create two UDP sockets, one second apart,
and connect them to the same remote host. Then, in a loop, I send two
packets through sock1, and one packet through sock2, with 5 seconds
between packets. I launch three instances of that program.
Instance 1
sock1
local port = 1077
initial IP ID = 62546
sock2
local port = 1078
initial IP ID = 62648
Instance 2
sock1
local port = 1079
initial IP ID = 64830
sock2
local port = 1080
initial IP ID = 64931
Instance 3
sock1
local port = 1081
initial IP ID = 776
sock2
local port = 1082
initial IP ID = 877
I'll examine the Linux source code, but it appears that the initial ID
is a function of the time when the socket is created or connected. The
ID field is incremented by 1 for each packet sent through a socket.
I ran tcpdump -nvvv -i eth0 port 40000
$ grep "\.1077" dump | cut -d' ' -f-10
12:17:40.563635 IP (tos 0x0, ttl 1, id 62546,
12:17:45.573006 IP (tos 0x0, ttl 1, id 62547,
12:17:55.591842 IP (tos 0x0, ttl 1, id 62548,
12:18:00.601264 IP (tos 0x0, ttl 1, id 62549,
12:18:10.620132 IP (tos 0x0, ttl 1, id 62550,
12:18:15.629520 IP (tos 0x0, ttl 1, id 62551,
12:18:25.648386 IP (tos 0x0, ttl 1, id 62552,
12:18:30.657779 IP (tos 0x0, ttl 1, id 62553,
12:18:40.686611 IP (tos 0x0, ttl 1, id 62554,
12:18:45.696031 IP (tos 0x0, ttl 1, id 62555,
12:18:55.714867 IP (tos 0x0, ttl 1, id 62556,
12:19:00.724288 IP (tos 0x0, ttl 1, id 62557,
12:19:10.743128 IP (tos 0x0, ttl 1, id 62558,
12:19:15.752549 IP (tos 0x0, ttl 1, id 62559,
12:19:25.771417 IP (tos 0x0, ttl 1, id 62560,
12:19:30.780803 IP (tos 0x0, ttl 1, id 62561,
$ grep "\.1078" dump | cut -d' ' -f-10
12:17:50.582424 IP (tos 0x0, ttl 1, id 62648,
12:18:05.610684 IP (tos 0x0, ttl 1, id 62649,
12:18:20.638938 IP (tos 0x0, ttl 1, id 62650,
12:18:35.667203 IP (tos 0x0, ttl 1, id 62651,
12:18:50.705452 IP (tos 0x0, ttl 1, id 62652,
12:19:05.733707 IP (tos 0x0, ttl 1, id 62653,
12:19:20.761965 IP (tos 0x0, ttl 1, id 62654,
12:19:35.790221 IP (tos 0x0, ttl 1, id 62655,
$ grep "\.1081" dump | cut -d' ' -f-10
12:18:18.209456 IP (tos 0x0, ttl 1, id 776,
12:18:23.218639 IP (tos 0x0, ttl 1, id 777,
12:18:33.237500 IP (tos 0x0, ttl 1, id 778,
12:18:38.246899 IP (tos 0x0, ttl 1, id 779,
12:18:48.275733 IP (tos 0x0, ttl 1, id 780,
12:18:53.285351 IP (tos 0x0, ttl 1, id 781,
12:19:03.303991 IP (tos 0x0, ttl 1, id 782,
12:19:08.313409 IP (tos 0x0, ttl 1, id 783,
12:19:18.332249 IP (tos 0x0, ttl 1, id 784,
12:19:23.341671 IP (tos 0x0, ttl 1, id 785,
12:19:33.360502 IP (tos 0x0, ttl 1, id 786,
>>> You should also keep in mind that the IP ID field in IPv4 is only
>>> 16 bits, and if you send your packets at a particularly large high
>>> enough rate can wrap very quickly.
>>
>> This is true whether I plan to give the ID field a special meaning
>> or not, so I'm not quite sure what to make of your statement :-)
>
> True, but except for the case of IP fragmentation, nothing else tries
> to assign any meaning to the IP ID field, so its wrapping is of no
> concern to them.
Point taken.
>>> Given that a user of UDP must assume that his datagrams can be
>>> duplicated and delayed, you would want to make sure that you didn't
>>> send more than 65535 datagrams in the length of time you can be
>>> statistically certain that any previous UDP datagram in an IP
>>> datagram with that same ID has "left the network." Otherwise, you
>>> become vulnerable to seeing an OLD datagram and thinking it is
>>> current.
>>
>> As far as I understand, if the IP packets are not fragmented, then
>> the ID field is mostly ignored. Is this a misconception?
>>
>> http://en.wikipedia.org/wiki/IPv4 states:
>>
>> This field is an identification field and is primarily used for
>> uniquely identifying fragments of an original IP datagram. Some
>> experimental work has suggested using the ID field for other
>> purposes, such as for adding packet-tracing information to datagrams
>> in order to help trace back datagrams with spoofed source addresses.
>
> That is substantially correct. In the long ago, it might have also
> been used by applications making use of raw IP services.
>
>>> That length of time would include the time it takes for IP fragment
>>> reassembly to time-out on your receiver. Further issues involving
>>> IP possibly reassembling fragments from different datagrams (again
>>> related to how quickly the measly 16 bit ID field can wrap) may be
>>> involved as well.
>
>> Again, the issue exists whether I give the ID field a special
>> meaning or not. I suppose users of UDP just live with it.
>
> Many folks don't even know about it. In theory the UDP checksum
> should protect them from the resulting "frankengrams."
I like that concept! :-)
>> A work-around might be to set the DF flag and handle ICMP errors in
>> the application.
>
> If you even ever see the ICMP messages coming back. Some sites chose
> to filter them in the belief it makes their situation more secure.
I've noticed that, by default, the "Don't Fragment" bit is set.
>>> Given the above, and the observation that the IP ID wasn't really
>>> (IIRC) meant to be "seen" outside of IP you probably should find
>>> some way to include a (larger) sequence number in the UDP datagram
>>> itself.
>>
>> I need a side-channel because I have to provide information to a
>> custom application while remaining compatible with legacy systems
>> that provide no extension mechanisms.
>
> Is it possible to put new front-ends in front of the legacy systems?
I'm afraid not.
>> Perhaps I could use an IP option? I'll take a look at RFC 791.
>
> Similarly, firewalls and the like often will take a dim view of the
> presence of IP options. They go to lengths to preclude side-channels
> in the first place.
Are firewalls known to drop packets with an experimental IP option?
Or would they just strip the option?
http://www.iana.org/assignments/ip-parameters
Copy Class Number Value Name Reference
---- ----- ------ ----- ------------------------------- ------------
0 0 30 30 EXP - RFC3692-style Experiment (**) [RFC4727]
0 2 30 94 EXP - RFC3692-style Experiment (**) [RFC4727]
1 0 30 158 EXP - RFC3692-style Experiment (**) [RFC4727]
1 2 30 222 EXP - RFC3692-style Experiment (**) [RFC4727]
> If you can live with the IP ID appearing to jump in value - ie that
> you may not see a contiguous, monotonically increasing IP ID then you
> can I suppose give it a try. Bear in mind though that nothing in the
> specs (IIRC) actually requires that the IP ID be monotonically
> increasing - only that it shouldn't be reused for the maximum packet
> lifetime between a pair of local/remote IP and I suspect protocol
> (eg UDP, TCP etc)
The IP stack in Linux seems to behave as I had hoped :-)
Here's my test program.
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h> // memset
#define ZERO(type, var) type var; memset(&var, 0, sizeof var)
int init_socket(void)
{
int sock;
if ((sock = socket(PF_INET, SOCK_DGRAM, 0)) < 0)
{
perror("socket"); exit(1);
}
ZERO(struct sockaddr_in, addr);
addr.sin_family = AF_INET;
addr.sin_port = htons(40000);
inet_aton("239.1.1.1", &addr.sin_addr);
if (connect(sock, (struct sockaddr *)&addr, sizeof addr) != 0)
{
perror("connect"); exit(2);
}
return sock;
}
#define SOCK_COUNT 2
#define PACKET_COUNT 20
#define PACKET_SIZE 100
char buf[PACKET_SIZE];
int main(int argc, char **argv)
{
int i, sock[SOCK_COUNT];
for (i=0; i < SOCK_COUNT; ++i)
{
sock[i] = init_socket();
sleep(1);
}
for (i=0; i < PACKET_COUNT; ++i)
{
if (send(sock[0], buf, argc, 0) < 0) perror("send");
sleep(5);
if (send(sock[0], buf, argc, 0) < 0) perror("send");
sleep(5);
if (send(sock[1], buf, argc, 0) < 0) perror("send");
sleep(5);
}
return 0;
}
Regards.