Re: [BEHAVE] (no subject)

Simon Perreault <simon.perreault@viagenie.ca> Thu, 27 June 2013 12:23 UTC

Return-Path: <simon.perreault@viagenie.ca>
X-Original-To: behave@ietfa.amsl.com
Delivered-To: behave@ietfa.amsl.com
Received: from localhost (localhost [127.0.0.1]) by ietfa.amsl.com (Postfix) with ESMTP id CBEDD21F9D0A for <behave@ietfa.amsl.com>; Thu, 27 Jun 2013 05:23:50 -0700 (PDT)
X-Virus-Scanned: amavisd-new at amsl.com
X-Spam-Flag: NO
X-Spam-Score: -0.306
X-Spam-Level:
X-Spam-Status: No, score=-0.306 tagged_above=-999 required=5 tests=[AWL=-2.294, BAYES_00=-2.599, FRT_STOCK2=3.988, J_CHICKENPOX_63=0.6, NO_RELAYS=-0.001]
Received: from mail.ietf.org ([12.22.58.30]) by localhost (ietfa.amsl.com [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id flaz1W65Xjrx for <behave@ietfa.amsl.com>; Thu, 27 Jun 2013 05:23:50 -0700 (PDT)
Received: from jazz.viagenie.ca (jazz.viagenie.ca [IPv6:2620:0:230:8000::2]) by ietfa.amsl.com (Postfix) with ESMTP id 71E7A21F9CDD for <behave@ietf.org>; Thu, 27 Jun 2013 05:23:47 -0700 (PDT)
Received: from [IPv6:::1] (unknown [IPv6:2001:660:3001:4012:7ddf:d947:bc5f:fe38]) by jazz.viagenie.ca (Postfix) with ESMTPSA id 9D9E14150E; Thu, 27 Jun 2013 08:23:46 -0400 (EDT)
Message-ID: <51CC2ED4.7090506@viagenie.ca>
Date: Thu, 27 Jun 2013 14:23:48 +0200
From: Simon Perreault <simon.perreault@viagenie.ca>
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:17.0) Gecko/20130620 Thunderbird/17.0.7
MIME-Version: 1.0
To: ivan@cacaoweb.org
References: <CB1B483277FEC94E9B58357040EE5D02325A6E93@xmb-rcd-x15.cisco.com> <2f7dce8264c8a9a72640629502a44295@cacaoweb.org> <51C1681A.5030909@viagenie.ca> <f8741fad1af1cee094de9c59408b7425@cacaoweb.org> <51C40374.8080403@viagenie.ca> <21e25b7ae1501228a67656b2fa4bc009@cacaoweb.org> <51CAA20F.4070307@viagenie.ca> <88c0ada2b8ebad078fb249ac6572fd8b@cacaoweb.org> <51CBD188.4060408@viagenie.ca> <fc3c7389e9fc7afc9201f0516de436a7@cacaoweb.org>
In-Reply-To: <fc3c7389e9fc7afc9201f0516de436a7@cacaoweb.org>
Content-Type: text/plain; charset=UTF-8; format=flowed
Content-Transfer-Encoding: 8bit
Cc: behave@ietf.org
Subject: Re: [BEHAVE] (no subject)
X-BeenThere: behave@ietf.org
X-Mailman-Version: 2.1.12
Precedence: list
List-Id: mailing list of BEHAVE IETF WG <behave.ietf.org>
List-Unsubscribe: <https://www.ietf.org/mailman/options/behave>, <mailto:behave-request@ietf.org?subject=unsubscribe>
List-Archive: <http://www.ietf.org/mail-archive/web/behave>
List-Post: <mailto:behave@ietf.org>
List-Help: <mailto:behave-request@ietf.org?subject=help>
List-Subscribe: <https://www.ietf.org/mailman/listinfo/behave>, <mailto:behave-request@ietf.org?subject=subscribe>
X-List-Received-Date: Thu, 27 Jun 2013 12:23:51 -0000

Le 2013-06-27 13:29, ivan c a écrit :
>> s1 = socket(..., SOCK_DGRAM, ...);
>> s2 = socket(..., SOCK_DGRAM, ...);
>> connect(s1, ...);
>> connect(s2, ...);
>>
>> The kernel is free to use the same local endpoint for those two sockets
>> as long as the remote endpoint is different.
>>
>> If you really want to force reusing the same local endpoint, you do the
>> SO_REUSEADDR+bind() dance before calling connect().
>
> Your pseudo-code will never bind the sockets on the same local endpoint.
> It is forbidden POSIX behavior.

I'm ready to accept that, with a proper citation.

> The option SO_REUSEADDR won't help either, this option is used to bind
> over an *already closed* socket in TIME_WAIT state, that's all.

I humbly believe that you are mistaken. It works, try it. This is a full 
example:

#include <netinet/in.h>
#include <string.h>

int
main()
{
         struct sockaddr_in       sin;
         int                      s1, s2, on = 1;

         memset(&sin, 0, sizeof(sin));
         sin.sin_family = AF_INET;
         sin.sin_addr.s_addr = htonl(0xc1319f4e);
         sin.sin_port = htons(12345);

         s1 = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
         s2 = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);

         setsockopt(s1, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
         setsockopt(s2, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));

         bind(s1, (struct sockaddr *)&sin, sizeof(sin));
         bind(s2, (struct sockaddr *)&sin, sizeof(sin));

         sin.sin_addr.s_addr = htonl(0xce7b1f02);
         sin.sin_port = htons(53);
         connect(s1, (struct sockaddr *)&sin, sizeof(sin));

         sin.sin_addr.s_addr = htonl(0x42e42d6e);
         sin.sin_port = htons(53);
         connect(s2, (struct sockaddr *)&sin, sizeof(sin));

         return 0;
}

Here's the output of strace'ing that program on Linux:

> socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP) = 3
> socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP) = 4
> setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
> setsockopt(4, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
> bind(3, {sa_family=AF_INET, sin_port=htons(12345), sin_addr=inet_addr("193.49.159.78")}, 16) = 0
> bind(4, {sa_family=AF_INET, sin_port=htons(12345), sin_addr=inet_addr("193.49.159.78")}, 16) = 0
> connect(3, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("206.123.31.2")}, 16) = 0
> connect(4, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("66.228.45.110")}, 16) = 0

connect()ing UDP sockets has two big advantages:

1) It allows using the same programming model for UDP and TCP. More code 
can be factored out, which is good software engineering.

2) It propagates ICMP errors up through the BSD API, allowing 
straightforward error handling that is again similar to TCP and can be 
factored out. For non-connected UDP sockets, ICMP errors are simply ignored.

Since file descriptors are basically free, come at no additional 
performance cost when you use something like epoll, kqueue, or windows 
IOCP, and since this method does not consume additional ports, I 
recommend it over sendto()/recvfrom() when I teach network programming.

This comes from W. Richard Stevens "UNIX Network Programming". The 
technique is used a lot in the wild. I'm not inventing anything.

Simon