Hi Julien,
On Tue, Nov 09, 2010 at 05:41:50PM +0000, Julien Massot wrote:
> Zeroconf implementation conform to rfc 3927.
> Ready for a review
Before reviewing the details, I'd like the following to happen:
- s/gzcip/ipv4ll/
- The IPv4LL steps should be integrated into gdhcp/client.c, as a fallback
step when not receiving a DHCP lease.
- I'm really fine with having a separate gdhcp/ipv4ll.c file for not stuffing
everything into gdhcp/client.c.
Now, the details:
> +typedef enum _listen_mode {
> + L_NONE,
> + L_ARP,
> +} ListenMode;
> +
> +typedef enum _zcip_client_state {
> + PROBE,
> + ANNOUNCE,
> + MONITOR,
> + DEFEND,
> +} ClientState;
You could add those to the ClientState ones, maybe with an IPV4LL_ prefix.
> +struct _GZCIPClient {
> + GZCIPType type;
> + ClientState state;
> + int ifindex;
> + uint8_t mac_address[6];
> + struct in_addr requested_ip;
> + struct in_addr null_ip;
> + uint8_t null_addr[6];
I don't think you need to carry a NULL MAC address with all your clients, see
my comments on the arp() routine.
> + uint8_t broadcast_addr[6];
This one is not needed.
> + char* assigned_ip;
> + int sockfd;
> + struct sockaddr_ll saddr;
> + uint8_t retry_times;
> + guint timeout;
> + guint conflicts;
> + ListenMode listen_mode;
> + int listener_sockfd;
> + GIOChannel *listener_channel;
> + guint listener_watch;
> + GZCIPClientEventFunc lease_available_cb;
> + GZCIPClientEventFunc lease_lost_cb;
Instead of those, we should have ipv4ll specific callbacks for letting the
gdhcp user know about it. For example, ipv4ll_available_cb and ipv4ll_lost_cb.
This way the dhcp plugin will be able to make his own decision.
> + GZCIPClientEventFunc address_conflict_cb;
This one is not needed.
> + gpointer lease_lost_data;
> + gpointer address_conflict_data;
> + gpointer lease_available_data;
> + GZCIPDebugFunc debug_func;
> + gpointer debug_data;
> +};
>
> +static gboolean probe_timeout(gpointer zcip_data);
> +static gboolean announce_timeout(gpointer zcip_data);
> +static gboolean defend_timeout(gpointer zcip_data);
> +
> +static void out(const char *str, gpointer data)
> +{
> + printf("%s", str);
> +}
This piece of code doesn't seem to be used.
> +static inline void debug(GZCIPClient *zcip_client, const char *format, ...)
> +{
> + char str[256];
> + va_list ap;
> +
> + if (zcip_client->debug_func == NULL)
> + return;
> +
> + va_start(ap, format);
> +
> + if (vsnprintf(str, sizeof(str), format, ap) > 0)
> + zcip_client->debug_func(str, zcip_client->debug_data);
> +
> + va_end(ap);
> +}
This will be the client.c debug routine.
> +
> +/**
> + * Pick a random link local IP address on 169.254/16, except that
> + * the first and last 256 addresses are reserved.
> + *
> + */
> +static uint32_t pick(void)
Let's be more explicit and call this one random_ip().
> +{
> + unsigned tmp;
> + struct timeval tv;
> + do {
> + gettimeofday(&tv, NULL);
> + srand(tv.tv_usec);
> + tmp = rand();
> + tmp = tmp & IN_CLASSB_HOST;
> + } while (tmp > (IN_CLASSB_HOST - 0x0200));
> + return ((LINKLOCAL_ADDR + 0x0100) + tmp);
It would be neat if we could call srand() with e.g. the last 2 interface MAC
address bytes, before calling rand(). That would give our ipv4ll
implementation a chance to assign the same ipv4ll IP to the same interface
over a reboot.
> +}
> + /* static uint32_t pick(void){ */
> + /* uint8_t tmp[4]; */
> + /* uint32_t addr; */
> + /* tmp[3] = 192; */
> + /* tmp[2] = 168; */
> + /* tmp[1] = 1; */
> + /* tmp[0] = 1; */
> +
> + /* memcpy(&addr, tmp, sizeof(addr)); */
> + /* return addr; */
> + /* } */
Please remove commented code.
> +char *g_zcip_client_get_address(GZCIPClient *zcip_client)
> +{
> + return g_strdup(zcip_client->assigned_ip);
> +}
You could use the client.c routine for this one.
> +char *g_zcip_client_get_netmask(GZCIPClient *zcip_client)
> +{
> + return g_strdup("255.255.0.0");
> +}
For this one, you could add it to client.c, and that should return
g_dhcp_client_get_option(dhcp_client, G_DHCP_SUBNET) or
g_strdup("255.255.0.0"), depending if we're a local link address or not.
> +static int switch_listening_mode(GZCIPClient *zcip_client,
> + ListenMode listen_mode){
> +
> + GIOChannel *listener_channel;
> + int listener_sockfd;
> +
> + if (zcip_client->listen_mode == listen_mode)
> + return 0;
> +
> + debug(zcip_client, "switching listen from %d to %d\n",
> + zcip_client->listen_mode, listen_mode);
> + if (zcip_client->listen_mode != L_NONE) {
> + g_source_remove(zcip_client->listener_watch);
> + zcip_client->listener_channel = NULL;
> + zcip_client->listen_mode = L_NONE;
> + zcip_client->listener_sockfd = -1;
> + zcip_client->listener_watch = 0;
> + }
> + if (listen_mode == L_NONE)
> + return 0;
> +
> + if (listen_mode != L_ARP)
> + return -EIO;
> +
> + listener_sockfd = zcip_socket(zcip_client->ifindex);
> +
> + if (listener_sockfd < 0)
> + return -EIO;
> +
> + listener_channel = g_io_channel_unix_new(listener_sockfd);
> + if (listener_channel == NULL) {
> + close(listener_sockfd);
> + return -EIO;
> + }
> + zcip_client->listen_mode = listen_mode;
> + zcip_client->listener_sockfd = listener_sockfd;
> + zcip_client->listener_channel = listener_channel;
> +
> + g_io_channel_set_close_on_unref(listener_channel, TRUE);
> + zcip_client->listener_watch =
> + g_io_add_watch_full(listener_channel,
> + G_PRIORITY_HIGH, G_IO_IN,
You need to support G_IO_NVAL | G_IO_ERR | G_IO_HUP as well, or you could end
up polling forever.
I realize this is an issue in client.c, please fix it there.
> + listener_event, zcip_client,
> + NULL);
> + g_io_channel_unref(zcip_client->listener_channel);
> +
> + return 0;
> +}
So I suppose you'll need to enhance the existing client.c one to support L_ARP
mode.
> +static void arp(GZCIPClient *zcip_client, struct in_addr source_ip,
> + uint8_t *target_eth, struct in_addr target_ip){
2 remarks:
- Please add the '{' after a line break.
- Let's call this one send_arp_packet()
> +
> + struct ether_arp p;
> + uint32_t ip_source;
> + uint32_t ip_target;
> + ip_source = htonl(source_ip.s_addr);
> + ip_target = htonl(target_ip.s_addr);
> + memset(&p, 0, sizeof(p));
> + // arp request
Please use /* */ for comments.
> + p.arp_hrd = htons(ARPHRD_ETHER);
> + p.arp_pro = htons(ETHERTYPE_IP);
> + p.arp_hln = ETH_ALEN;
> + p.arp_pln = 4;
> + p.arp_op = htons(ARPOP_REQUEST);
> + memcpy(&p.arp_sha, zcip_client->mac_address, ETH_ALEN);
> + memcpy(&p.arp_spa, &ip_source, sizeof(p.arp_spa));
> + memcpy(&p.arp_tha, target_eth, ETH_ALEN);
You probably don't need the target_eth argument here, just memset it to 0.
> + memcpy(&p.arp_tpa, &ip_target, sizeof(p.arp_tpa));
> +
> + sendto((zcip_client->sockfd), &p, sizeof(p), 0,
> + (struct sockaddr*) &(zcip_client->saddr),
> + sizeof(zcip_client->saddr));
> +}
> +
> +static gboolean send_probe_packet(gpointer zcip_data)
> +{
> + GZCIPClient *zcip_client;
> + guint timeout;
> +
> + zcip_client = zcip_data;
> + /*if requested_ip is not valid, pick a new address*/
> + if ( zcip_client->requested_ip.s_addr == 0)
> + {
"if (bla) {" and not
"if (bla)
{"
> + debug(zcip_client, "pick a new address \n");
> + zcip_client->requested_ip.s_addr = pick();
> + }
> +
> + debug(zcip_client, "sending probe \n");
> +
> + arp(zcip_client, zcip_client->null_ip, zcip_client->null_addr,
> + zcip_client->requested_ip);
> +
> + if ( zcip_client->retry_times < 2 )
> + timeout =
> + random_delay_ms(PROBE_MAX-PROBE_MIN) + (PROBE_MIN*1000);
> + else
> + timeout = (ANNOUNCE_WAIT * 1000);
> +
> + zcip_client->timeout = g_timeout_add_full(G_PRIORITY_HIGH,
> + timeout,
> + probe_timeout,
> + zcip_client,
> + NULL);
Please fix your indentation here.
Also, you need to check for zcip_client->timeout not being 0. Same applies to
most of your g_timeout_add_full() calls in this patch.
> + switch_listening_mode(zcip_client, L_ARP);
> + zcip_client->retry_times++;
> + return FALSE;
> +}
> +
> +static gboolean send_announce_packet(gpointer zcip_data)
> +{
> + GZCIPClient *zcip_client;
> + zcip_client = zcip_data;
> + debug(zcip_client, "sending announce \n");
> + arp(zcip_client, zcip_client->requested_ip, zcip_client->null_addr,
> + zcip_client->requested_ip);
> + if (zcip_client->state == DEFEND){
> + zcip_client->timeout =
> + g_timeout_add_seconds_full(G_PRIORITY_HIGH,
> + DEFEND_INTERVAL,
> + defend_timeout,
> + zcip_client,
> + NULL);
> + return TRUE;
> + }
> + else
> + zcip_client->timeout =
> + g_timeout_add_seconds_full(G_PRIORITY_HIGH,
> + ANNOUNCE_INTERVAL,
> + announce_timeout,
> + zcip_client,
> + NULL);
> + zcip_client->retry_times++;
> + return TRUE;
> +}
> +
> +static int zcip_recv_arp_packet( GZCIPClient *zcip_client ){
> + int bytes;
> + struct ether_arp arp;
> + uint32_t ip_requested;
> + int source_conflict;
> + int target_conflict;
> +
> + memset(&arp, 0, sizeof(arp));
> + bytes = 0;
> + bytes = read(zcip_client->listener_sockfd, &arp, sizeof(arp));
> + if (bytes < 0)
> + return -1;
return bytes;
> +
> + if ( arp.arp_op != htons(ARPOP_REPLY) &&
if (arp.arp_op...
> + arp.arp_op != htons(ARPOP_REQUEST)){
> + return -1;
Please return a relevant error code, -EINVAL for example.
> + }
> +
> + ip_requested = ntohl(zcip_client->requested_ip.s_addr);
> +
> + source_conflict = memcmp(arp.arp_spa, &ip_requested,
> + sizeof(ip_requested));
> + if (source_conflict != 0){
No need for brackets here.
> + zcip_client->conflicts++;
> + }
> + target_conflict = memcmp(arp.arp_tpa, &ip_requested,
> + sizeof(ip_requested));
> +
> + if (source_conflict !=0 && target_conflict != 0)
!= 0 and not !=0
> + return -1;
Shouldn't that return 0 ? We have no conflicts, it's not an error.
> + debug(zcip_client, "conflict detected\n");
> + if (zcip_client->state == MONITOR){
> + if (source_conflict != 0)
> + return -1;
This doesn't seem to be an error neither.
> + zcip_client->state = DEFEND;
> + debug(zcip_client, "DEFEND mode conflicts : %d\n",
> + zcip_client->conflicts);
> + send_announce_packet(zcip_client);
> + return 0;
> + }
> + if (zcip_client->state == DEFEND){
> + if (source_conflict != 0)
> + return -1;
Ditto.
> + else if (zcip_client->lease_lost_cb != NULL)
> + zcip_client->lease_lost_cb(zcip_client,
> + zcip_client->lease_lost_data);
So, as I said, that should probably call an ipv4 specific hook.
> + }
> + g_zcip_client_stop(zcip_client);
> +
> + zcip_client->conflicts++;
> + if(zcip_client->conflicts < MAX_CONFLICTS)
if (), not if().
> + g_zcip_client_start(zcip_client);
> + else
So here we got a lot of conflicts. Maybe we should have an additional callback
to let the gdhcp user know about it, and decide if they want to continue
trying or not.
>From a UI point of view, it will look like we're configuring the interface for
ever, which is not nice. At some point we have to admit we failed.
> + g_timeout_add_seconds(RATE_LIMIT_INTERVAL,
> + g_zcip_client_start,
> + zcip_client);
> + return 0;
> +}
> +
> +static gboolean defend_timeout(gpointer zcip_data)
> +{
> + GZCIPClient *zcip_client = zcip_data;
> +
> + zcip_client->conflicts = 0;
> + debug(zcip_client, "back to MONITOR mode\n");
> + zcip_client->state = MONITOR;
> + switch_listening_mode(zcip_client, L_ARP);
> +
> + return FALSE;
> +}
> +
> +static char *get_ip(uint32_t ip)
> +{
> + struct in_addr addr;
> + addr.s_addr = ip;
> + return g_strdup(inet_ntoa(addr));
> +}
You should be able to get this one from client.c
> +static gboolean announce_timeout(gpointer zcip_data)
> +{
> + GZCIPClient *zcip_client = zcip_data;
> + uint32_t ip;
> +
> + debug(zcip_client, "request timeout (retries %d) \n",
> + zcip_client->retry_times);
> +
> + if( zcip_client->retry_times != ANNOUNCE_NUM ){
> + send_announce_packet(zcip_client);
> + return FALSE;
> + }
> +
> + ip = htonl(zcip_client->requested_ip.s_addr);
> + debug(zcip_client, "switching to monitor mode\n");
> + zcip_client->state = MONITOR;
> + if(zcip_client->assigned_ip == NULL)
> + zcip_client->assigned_ip = get_ip(ip);
> + if (zcip_client->lease_available_cb != NULL)
> + zcip_client->lease_available_cb(zcip_client,
> + zcip_client->lease_available_data);
Here again, I'd like to see an IPv4LL specific hook.
> + zcip_client->conflicts = 0;
> +
> + return FALSE;
> +}
> +
> +static gboolean probe_timeout(gpointer zcip_data)
> +{
> +
> + GZCIPClient *zcip_client = zcip_data;
> + debug(zcip_client, "request timeout (retries %d) \n",
> + zcip_client->retry_times);
> +
> + if( zcip_client->retry_times == PROBE_NUM ) {
if (bla) and not if( bla )
> + zcip_client->state = ANNOUNCE;
> + zcip_client->retry_times = 0;
> + send_announce_packet(zcip_client);
> + return FALSE;
> + }
> + send_probe_packet(zcip_client);
> + return FALSE;
> +}
> +
> +static gboolean listener_event(GIOChannel *channel, GIOCondition condition,
> + gpointer zcip_data)
> +
> +{
> + GZCIPClient *zcip_client = zcip_data;
> + int re;
> +
> + if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
> + zcip_client->listener_watch = 0;
> + return TRUE;
> + }
> +
> + if (zcip_client->listen_mode == L_NONE)
> + return FALSE;
> +
> + if (zcip_client->listener_sockfd < 0){
> + debug(zcip_client, "socket close \n");
> + return -EIO;
> + }
> + if (zcip_client->listen_mode == L_ARP)
> + re = zcip_recv_arp_packet(zcip_client);
> + return TRUE;
> +}
Here I suppose you'll have to integrate that into the client.c listening event
state machine.
So once you'll have this code integrated, it would be nice to get some
plugins/dhcp.c changes that will implement the ipv4ll specific hooks.
Cheers,
Samuel.
--
Intel Open Source Technology Centre
http://oss.intel.com/
_______________________________________________
connman mailing list
[email protected]
http://lists.connman.net/listinfo/connman