IPv4 multicast distribution support.

Message ID 1484596174-16341-1-git-send-email-jhaws@sdl.usu.edu (mailing list archive)
State Superseded, archived
Delegated to: Simon Wunderlich
Headers

Commit Message

Jonathan Haws Jan. 16, 2017, 7:49 p.m. UTC
  Enabled via -4 expert option where multicast group address is specified.
This option will disable IPv6 communication and is intended for use in networks
which do not support IPv6 addressing.

When this option is enabled, all IPv6 communication is disabled. Combining
IPv4 and IPv6 alfred nodes is unsupported.

Signed-off-by: Jonathan Haws <jhaws@sdl.usu.edu>
---
 README         |   4 +-
 alfred.h       |  21 +++++---
 batadv_query.c |  53 ++++++++++++-------
 batadv_query.h |   5 +-
 client.c       |   2 +-
 main.c         |  10 +++-
 man/alfred.8   |   5 ++
 netsock.c      | 160 +++++++++++++++++++++++++++++++++++++++++++++++++++++----
 recv.c         |  67 +++++++++++++++++-------
 send.c         |  51 ++++++++++++------
 server.c       |   8 +--
 unix_sock.c    |   2 +-
 12 files changed, 308 insertions(+), 80 deletions(-)
  

Comments

Sven Eckelmann Jan. 17, 2017, 7:44 a.m. UTC | #1
Patch subject prefix is wrong (actually, it is missing).

On Montag, 16. Januar 2017 12:49:34 CET Jonathan Haws wrote:
> +int ipv4_to_mac(const alfred_addr *addr, struct ether_addr *mac)
> +{
> +  mac->ether_addr_octet[0] = 0;
> +  mac->ether_addr_octet[1] = 0;
> +  mac->ether_addr_octet[2] = (addr->ipv4.s_addr >> 24) & 0xFF;
> +  mac->ether_addr_octet[3] = (addr->ipv4.s_addr >> 16) & 0xFF;
> +  mac->ether_addr_octet[4] = (addr->ipv4.s_addr >>  8) & 0xFF;
> +  mac->ether_addr_octet[5] = (addr->ipv4.s_addr >>  0) & 0xFF;
> +
> +  if (!is_valid_ether_addr(mac->ether_addr_octet))
> +    return -EINVAL;
> +
> +  return 0;
> +}


This will not return the mac address of the device. It will therefore break
the synchronization code. see SOURCE_FIRST_HAND in sync_data and the code
which sets data_source in finish_alfred_push_data.

On Montag, 16. Januar 2017 12:49:34 CET Jonathan Haws wrote:
> @@ -61,6 +62,7 @@ static void alfred_usage(void)
>         printf("                                      other masters\n");
>         printf("  -p, --sync-period [period]          set synchronization period, in seconds\n");
>         printf("                                      fractional seconds are supported (i.e. 0.2 = 5 Hz)\n");
> +       printf("  -4                                  specify IPv4 multicast address and operate in IPv4 mode");
>         printf("\n");
>         printf("  -u, --unix-path [path]              path to unix socket used for client-server\n");
>         printf("                                      communication (default: \""ALFRED_SOCK_PATH_DEFAULT"\")\n");


The documentation is wrong. It requires an argument but the argument is not
shown.

Same problem in the manpage.

On Montag, 16. Januar 2017 19:52:41 CET Jonathan Haws wrote:
> I realize that the code in this patch is not formatted properly, but I
> was unable to get checkpatch.pl to scan this right - it needs a full
> kernel tree.  Is there another formatting script I can run?

What is the problem with downloading the kernel sources? And auto-formatting
scripts tend to not get everything right and make things worse in some cases.
checkpatch.pl is therefore only to point out some obvious problems.

For example that you are replace tabs (8 spaces wide in alfred code) sometimes
with 2 spaces.

Kind regards,
	Sven
  
Jonathan Haws Jan. 17, 2017, 3:39 p.m. UTC | #2
> Patch subject prefix is wrong (actually, it is missing).


Good catch.  Just missed it.  I'll modify the patch with that included.

> > 

> > +int ipv4_to_mac(const alfred_addr *addr, struct ether_addr *mac)

> > +{

> > +  mac->ether_addr_octet[0] = 0;

> > +  mac->ether_addr_octet[1] = 0;

> > +  mac->ether_addr_octet[2] = (addr->ipv4.s_addr >> 24) & 0xFF;

> > +  mac->ether_addr_octet[3] = (addr->ipv4.s_addr >> 16) & 0xFF;

> > +  mac->ether_addr_octet[4] = (addr->ipv4.s_addr >>  8) & 0xFF;

> > +  mac->ether_addr_octet[5] = (addr->ipv4.s_addr >>  0) & 0xFF;

> > +

> > +  if (!is_valid_ether_addr(mac->ether_addr_octet))

> > +    return -EINVAL;

> > +

> > +  return 0;

> > +}

> 

> This will not return the mac address of the device. It will therefore

> break

> the synchronization code. see SOURCE_FIRST_HAND in sync_data and the

> code

> which sets data_source in finish_alfred_push_data.


You are correct - this does not return the MAC address of the device.
 Rather it uses the source IP address.  Since we're dealing with IPv4
in this case I believe it is safe to assume that the network has been
properly configured and the remote node will have a valid IPv4 address,
which is used here.  In my testing the synchronization and data sharing
worked properly and all data was shared as expected.

What would you like to see here?  Would you prefer an ARP request to go
and get the MAC address of the remote node?

> > 

> > @@ -61,6 +62,7 @@ static void alfred_usage(void)

> >         printf("                                      other

> > masters\n");

> >         printf("  -p, --sync-period [period]          set

> > synchronization period, in seconds\n");

> >         printf("                                      fractional

> > seconds are supported (i.e. 0.2 = 5 Hz)\n");

> > +       printf("  -4                                  specify IPv4

> > multicast address and operate in IPv4 mode");

> >         printf("\n");

> >         printf("  -u, --unix-path [path]              path to unix

> > socket used for client-server\n");

> >         printf("                                      communication

> > (default: \""ALFRED_SOCK_PATH_DEFAULT"\")\n");

> 

> The documentation is wrong. It requires an argument but the argument

> is not

> shown.

> 

> Same problem in the manpage.


Correct - again, my mistake.  I'll fix it in the updated patch.

> > I realize that the code in this patch is not formatted properly,

> > but I

> > was unable to get checkpatch.pl to scan this right - it needs a

> > full

> > kernel tree.  Is there another formatting script I can run?

> What is the problem with downloading the kernel sources? And auto-

> formatting

> scripts tend to not get everything right and make things worse in

> some cases.

> checkpatch.pl is therefore only to point out some obvious problems.


I did download the kernel source, but when I try to run the script it
either tells me that I don't have a valid kernel tree (when using the
sources for my Xubuntu kernel) or that the file I'm trying to scan
isn't part of the tree (when using the latest from kernel.org).  I've
never used that tool in the past - can you send me a usage example for
scanning a file in the alfred tree?

> For example that you are replace tabs (8 spaces wide in alfred code)

> sometimes

> with 2 spaces.


Yes - I'd love for checkpatch.pl to scan it and give me a list of what
to fix to make sure I don't miss anything.


Thanks!
  
Sven Eckelmann Jan. 17, 2017, 4:54 p.m. UTC | #3
On Dienstag, 17. Januar 2017 15:39:00 CET Jonathan Haws wrote:
[...]
> > > 
> > > +int ipv4_to_mac(const alfred_addr *addr, struct ether_addr *mac)
> > > +{
[...]
> > This will not return the mac address of the device. It will therefore
> > break
> > the synchronization code. see SOURCE_FIRST_HAND in sync_data and the
> > code
> > which sets data_source in finish_alfred_push_data.
> 
> You are correct - this does not return the MAC address of the device.
>  Rather it uses the source IP address.  Since we're dealing with IPv4
> in this case I believe it is safe to assume that the network has been
> properly configured and the remote node will have a valid IPv4 address,
> which is used here.  In my testing the synchronization and data sharing
> worked properly and all data was shared as expected.

It should not be synced between masters when the data was received from a 
slave (because the master will not detect it as FIRST_HAND). If it does still 
work then you have a bug somewhere else.

[...]
> What would you like to see here?  Would you prefer an ARP request to go
> and get the MAC address of the remote node?

Doesn't sound that nice. @Simon, do you have an opinion about this patch 
before Jonathan rewrites it? (It may take a while until he answers because he 
should be offline this week)

[...]
> > > I realize that the code in this patch is not formatted properly,
> > > but I
> > > was unable to get checkpatch.pl to scan this right - it needs a
> > > full
> > > kernel tree.  Is there another formatting script I can run?
> > What is the problem with downloading the kernel sources? And auto-
> > formatting
> > scripts tend to not get everything right and make things worse in
> > some cases.
> > checkpatch.pl is therefore only to point out some obvious problems.
> 
> I did download the kernel source, but when I try to run the script it
> either tells me that I don't have a valid kernel tree (when using the
> sources for my Xubuntu kernel) or that the file I'm trying to scan
> isn't part of the tree (when using the latest from kernel.org).  I've
> never used that tool in the past - can you send me a usage example for
> scanning a file in the alfred tree?

Just did this from my linux-next.git checkout:

    ./scripts/checkpatch.pl --strict --ignore=LONG_LINE_STRING \
      --ignore=LONG_LINE --ignore=LONG_LINE_COMMENT \
      --ignore=FSF_MAILING_ADDRESS \
      -f ../alfred/main.c

or:

    ./scripts/checkpatch.pl --strict --ignore=LONG_LINE_STRING \
      --ignore=LONG_LINE --ignore=LONG_LINE_COMMENT \
      --ignore=FSF_MAILING_ADDRESS \
      ../alfred/0001-IPv4-multicast-distribution-support.patch

Kind regards,
	Sven
  
Linus Lüssing Jan. 17, 2017, 7:11 p.m. UTC | #4
On Tue, Jan 17, 2017 at 05:54:57PM +0100, Sven Eckelmann wrote:
> > I did download the kernel source, but when I try to run the script it
> > either tells me that I don't have a valid kernel tree (when using the
> > sources for my Xubuntu kernel) or that the file I'm trying to scan
> > isn't part of the tree (when using the latest from kernel.org).  I've
> > never used that tool in the past - can you send me a usage example for
> > scanning a file in the alfred tree?
> 
> Just did this from my linux-next.git checkout:
> 
>     ./scripts/checkpatch.pl --strict --ignore=LONG_LINE_STRING \
>       --ignore=LONG_LINE --ignore=LONG_LINE_COMMENT \
>       --ignore=FSF_MAILING_ADDRESS \
>       -f ../alfred/main.c
> 
> or:
> 
>     ./scripts/checkpatch.pl --strict --ignore=LONG_LINE_STRING \
>       --ignore=LONG_LINE --ignore=LONG_LINE_COMMENT \
>       --ignore=FSF_MAILING_ADDRESS \
>       ../alfred/0001-IPv4-multicast-distribution-support.patch

Not 100% sure whether it's needed, but if you still have issues
try to at least call "make scripts" (or even "make prepare") in the
kernel source directory first.

Also, maybe simply use the vanilla tree from Torvalds with a
  $ git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git

Should have a more up-to-date checkpatch.pl than the Ubuntu source
directory anyway.
  
Linus Lüssing Jan. 17, 2017, 7:30 p.m. UTC | #5
On Mon, Jan 16, 2017 at 12:49:34PM -0700, Jonathan Haws wrote:
> Enabled via -4 expert option where multicast group address is specified.
> This option will disable IPv6 communication and is intended for use in networks
> which do not support IPv6 addressing.
> 
> When this option is enabled, all IPv6 communication is disabled. Combining
> IPv4 and IPv6 alfred nodes is unsupported.
> 
> Signed-off-by: Jonathan Haws <jhaws@sdl.usu.edu>
> ---
>  README         |   4 +-
>  alfred.h       |  21 +++++---
>  batadv_query.c |  53 ++++++++++++-------
>  batadv_query.h |   5 +-
>  client.c       |   2 +-
>  main.c         |  10 +++-
>  man/alfred.8   |   5 ++
>  netsock.c      | 160 +++++++++++++++++++++++++++++++++++++++++++++++++++++----
>  recv.c         |  67 +++++++++++++++++-------
>  send.c         |  51 ++++++++++++------
>  server.c       |   8 +--
>  unix_sock.c    |   2 +-
>  12 files changed, 308 insertions(+), 80 deletions(-)
> 
> diff --git a/README b/README
> index bc1c3bc..28f3dc9 100644
> --- a/README
> +++ b/README
> @@ -20,7 +20,9 @@ programs to communicate with alfred (done via unix sockets). alfred then takes
>  care of distributing the local information to other alfred servers on other
>  nodes. This is done via IPv6 link-local multicast, and does not require any
>  configuration. A user can request data from alfred, and will receive the
> -information available from all alfred servers in the network.
> +information available from all alfred servers in the network. Alternatively,
> +alfred can be configured to distribute the local information via IPv4 multicast.
> +This is configured by setting the IPv4 multicast group address in the -4 option.
>  
>  Compilation
>  -----------
> diff --git a/alfred.h b/alfred.h
> index 2e8db1e..5237594 100644
> --- a/alfred.h
> +++ b/alfred.h
> @@ -48,6 +48,12 @@ enum data_source {
>  	SOURCE_SYNCED = 2,
>  };
>  
> +typedef union
> +{
> +  struct in_addr ipv4;
> +  struct in6_addr ipv6;
> +} alfred_addr;
> +

Just out of curiousity (and I suspect the answer is probably "no").
For multicast there is no API to write protocol agnostic socket
code, right?

(like these agnostic sockaddr_storage / getaddrinfo() / inet_pton()
thingies which you would use for unicast)
  
Jonathan Haws Jan. 18, 2017, 5:06 a.m. UTC | #6
> >  

> > +typedef union

> > +{

> > +  struct in_addr ipv4;

> > +  struct in6_addr ipv6;

> > +} alfred_addr;

> > +

> Just out of curiousity (and I suspect the answer is probably "no").

> For multicast there is no API to write protocol agnostic socket

> code, right?

> 

> (like these agnostic sockaddr_storage / getaddrinfo() / inet_pton()

> thingies which you would use for unicast)


To be honest I didn't know about sockaddr_storage - I haven't done much
of any IPv6 socket programming.

Looking at what I changed, it would all be changed in a very similar
fashion using sockaddr_storage instead of a new alfred_addr - just
could have left out the typedef union.  I would have had to cast things
inside the other routines rather than just accessing the union member.
 I don't know if there is a preference one way or the other, but they
seem to be doing exactly the same thing in my mind.
  
Jonathan Haws Jan. 18, 2017, 5:12 a.m. UTC | #7
> > > > +int ipv4_to_mac(const alfred_addr *addr, struct ether_addr

> > > > *mac)

> > > > +{

> [...]

> > 

> > > 

> > > This will not return the mac address of the device. It will

> > > therefore

> > > break

> > > the synchronization code. see SOURCE_FIRST_HAND in sync_data and

> > > the

> > > code

> > > which sets data_source in finish_alfred_push_data.

> > You are correct - this does not return the MAC address of the

> > device.

> >  Rather it uses the source IP address.  Since we're dealing with

> > IPv4

> > in this case I believe it is safe to assume that the network has

> > been

> > properly configured and the remote node will have a valid IPv4

> > address,

> > which is used here.  In my testing the synchronization and data

> > sharing

> > worked properly and all data was shared as expected.

> It should not be synced between masters when the data was received

> from a 

> slave (because the master will not detect it as FIRST_HAND). If it

> does still 

> work then you have a bug somewhere else.

> 

> [...]

> > 

> > What would you like to see here?  Would you prefer an ARP request

> > to go

> > and get the MAC address of the remote node?

> Doesn't sound that nice. @Simon, do you have an opinion about this

> patch 

> before Jonathan rewrites it? (It may take a while until he answers

> because he 

> should be offline this week)


I got thinking about the testing I did and realized that I tested in a
setup where every node was set to master - there were no slaves.  My
testing consisted of the following:

Node A:
$ cat hostname | alfred -s 100

Node B:
$ alfred -r 100

I was able to see the entry from the remote node (with the correct MAC
address) in the query result.

Is there a typical test routine that is used to ensure proper operation
of alfred?  Should I simply add a third node to my setup and configure
it as a slave and make sure things operate properly or is there a more
automated test or better approach?
  
Sven Eckelmann Jan. 18, 2017, 8:11 a.m. UTC | #8
On Mittwoch, 18. Januar 2017 05:12:12 CET Jonathan Haws wrote:
> Is there a typical test routine that is used to ensure proper operation
> of alfred?  Should I simply add a third node to my setup and configure
> it as a slave and make sure things operate properly or is there a more
> automated test or better approach?

I would not be aware of some special test setup which is used for alfred.

And yes, you could simply add a slave to your setup which also tries to insert 
data. All master nodes should then be able to retrieve the data from this 
slave (after sync).

Kind regards,
	Sven
  

Patch

diff --git a/README b/README
index bc1c3bc..28f3dc9 100644
--- a/README
+++ b/README
@@ -20,7 +20,9 @@  programs to communicate with alfred (done via unix sockets). alfred then takes
 care of distributing the local information to other alfred servers on other
 nodes. This is done via IPv6 link-local multicast, and does not require any
 configuration. A user can request data from alfred, and will receive the
-information available from all alfred servers in the network.
+information available from all alfred servers in the network. Alternatively,
+alfred can be configured to distribute the local information via IPv4 multicast.
+This is configured by setting the IPv4 multicast group address in the -4 option.
 
 Compilation
 -----------
diff --git a/alfred.h b/alfred.h
index 2e8db1e..5237594 100644
--- a/alfred.h
+++ b/alfred.h
@@ -48,6 +48,12 @@  enum data_source {
 	SOURCE_SYNCED = 2,
 };
 
+typedef union
+{
+  struct in_addr ipv4;
+  struct in6_addr ipv6;
+} alfred_addr;
+
 struct dataset {
 	struct alfred_data data;
 	unsigned char *buf;
@@ -80,7 +86,7 @@  struct transaction_head {
 
 struct server {
 	struct ether_addr hwaddr;
-	struct in6_addr address;
+	alfred_addr address;
 	struct timespec last_seen;
 	uint8_t tq;
 };
@@ -100,7 +106,7 @@  enum clientmode {
 
 struct interface {
 	struct ether_addr hwaddr;
-	struct in6_addr address;
+	alfred_addr address;
 	uint32_t scope_id;
 	char *interface;
 	int netsock;
@@ -122,6 +128,7 @@  struct globals {
 	int clientmode_arg;
 	int clientmode_version;
 	int verbose;
+	int ipv4mode;
 
 	int unix_sock;
 	const char *unix_path;
@@ -142,7 +149,7 @@  struct globals {
 
 #define MAX_PAYLOAD ((1 << 16) - 1 - sizeof(struct udphdr))
 
-extern const struct in6_addr in6addr_localmcast;
+extern alfred_addr alfred_mcast;
 
 /* server.c */
 int alfred_server(struct globals *globals);
@@ -169,13 +176,13 @@  static inline bool transaction_finished(struct transaction_head *head)
 
 /* send.c */
 int push_data(struct globals *globals, struct interface *interface,
-	      struct in6_addr *destination, enum data_source max_source_level,
+	      alfred_addr *destination, enum data_source max_source_level,
 	      int type_filter, uint16_t tx_id);
 int announce_master(struct globals *globals);
 int push_local_data(struct globals *globals);
 int sync_data(struct globals *globals);
-ssize_t send_alfred_packet(struct interface *interface,
-			   const struct in6_addr *dest, void *buf, int length);
+ssize_t send_alfred_packet(struct globals *globals, struct interface *interface,
+			   const alfred_addr *dest, void *buf, int length);
 /* unix_sock.c */
 int unix_sock_read(struct globals *globals);
 int unix_sock_open_daemon(struct globals *globals);
@@ -195,7 +202,7 @@  int netsock_prepare_select(struct globals *globals, fd_set *fds, int maxsock);
 void netsock_check_error(struct globals *globals, fd_set *errfds);
 int netsock_receive_packet(struct globals *globals, fd_set *fds);
 int netsock_own_address(const struct globals *globals,
-			const struct in6_addr *address);
+			const alfred_addr *address);
 /* util.c */
 int time_diff(struct timespec *tv1, struct timespec *tv2,
 	      struct timespec *tvdiff);
diff --git a/batadv_query.c b/batadv_query.c
index ac45b9b..ae91d2e 100644
--- a/batadv_query.c
+++ b/batadv_query.c
@@ -80,22 +80,22 @@  out:
 	return ret;
 }
 
-int mac_to_ipv6(const struct ether_addr *mac, struct in6_addr *addr)
+int mac_to_ipv6(const struct ether_addr *mac, alfred_addr *addr)
 {
 	memset(addr, 0, sizeof(*addr));
-	addr->s6_addr[0] = 0xfe;
-	addr->s6_addr[1] = 0x80;
+	addr->ipv6.s6_addr[0] = 0xfe;
+	addr->ipv6.s6_addr[1] = 0x80;
 
-	addr->s6_addr[8] = mac->ether_addr_octet[0] ^ 0x02;
-	addr->s6_addr[9] = mac->ether_addr_octet[1];
-	addr->s6_addr[10] = mac->ether_addr_octet[2];
+	addr->ipv6.s6_addr[8] = mac->ether_addr_octet[0] ^ 0x02;
+	addr->ipv6.s6_addr[9] = mac->ether_addr_octet[1];
+	addr->ipv6.s6_addr[10] = mac->ether_addr_octet[2];
 
-	addr->s6_addr[11] = 0xff;
-	addr->s6_addr[12] = 0xfe;
+	addr->ipv6.s6_addr[11] = 0xff;
+	addr->ipv6.s6_addr[12] = 0xfe;
 
-	addr->s6_addr[13] = mac->ether_addr_octet[3];
-	addr->s6_addr[14] = mac->ether_addr_octet[4];
-	addr->s6_addr[15] = mac->ether_addr_octet[5];
+	addr->ipv6.s6_addr[13] = mac->ether_addr_octet[3];
+	addr->ipv6.s6_addr[14] = mac->ether_addr_octet[4];
+	addr->ipv6.s6_addr[15] = mac->ether_addr_octet[5];
 
 	return 0;
 }
@@ -118,17 +118,17 @@  int is_ipv6_eui64(const struct in6_addr *addr)
 	return 1;
 }
 
-int ipv6_to_mac(const struct in6_addr *addr, struct ether_addr *mac)
+int ipv6_to_mac(const alfred_addr *addr, struct ether_addr *mac)
 {
-	if (!is_ipv6_eui64(addr))
+	if (!is_ipv6_eui64(&addr->ipv6))
 		return -EINVAL;
 
-	mac->ether_addr_octet[0] = addr->s6_addr[8] ^ 0x02;
-	mac->ether_addr_octet[1] = addr->s6_addr[9];
-	mac->ether_addr_octet[2] = addr->s6_addr[10];
-	mac->ether_addr_octet[3] = addr->s6_addr[13];
-	mac->ether_addr_octet[4] = addr->s6_addr[14];
-	mac->ether_addr_octet[5] = addr->s6_addr[15];
+	mac->ether_addr_octet[0] = addr->ipv6.s6_addr[8] ^ 0x02;
+	mac->ether_addr_octet[1] = addr->ipv6.s6_addr[9];
+	mac->ether_addr_octet[2] = addr->ipv6.s6_addr[10];
+	mac->ether_addr_octet[3] = addr->ipv6.s6_addr[13];
+	mac->ether_addr_octet[4] = addr->ipv6.s6_addr[14];
+	mac->ether_addr_octet[5] = addr->ipv6.s6_addr[15];
 
 	if (!is_valid_ether_addr(mac->ether_addr_octet))
 		return -EINVAL;
@@ -136,6 +136,21 @@  int ipv6_to_mac(const struct in6_addr *addr, struct ether_addr *mac)
 	return 0;
 }
 
+int ipv4_to_mac(const alfred_addr *addr, struct ether_addr *mac)
+{
+  mac->ether_addr_octet[0] = 0;
+  mac->ether_addr_octet[1] = 0;
+  mac->ether_addr_octet[2] = (addr->ipv4.s_addr >> 24) & 0xFF;
+  mac->ether_addr_octet[3] = (addr->ipv4.s_addr >> 16) & 0xFF;
+  mac->ether_addr_octet[4] = (addr->ipv4.s_addr >>  8) & 0xFF;
+  mac->ether_addr_octet[5] = (addr->ipv4.s_addr >>  0) & 0xFF;
+
+  if (!is_valid_ether_addr(mac->ether_addr_octet))
+    return -EINVAL;
+
+  return 0;
+}
+
 static int batadv_interface_check_debugfs(const char *mesh_iface)
 {
 	char full_path[MAX_PATH + 1];
diff --git a/batadv_query.h b/batadv_query.h
index 0df8a35..ec69290 100644
--- a/batadv_query.h
+++ b/batadv_query.h
@@ -28,8 +28,9 @@  struct ether_addr *translate_mac(const char *mesh_iface,
 				 const struct ether_addr *mac);
 uint8_t get_tq(const char *mesh_iface, struct ether_addr *mac);
 int batadv_interface_check(const char *mesh_iface);
-int mac_to_ipv6(const struct ether_addr *mac, struct in6_addr *addr);
-int ipv6_to_mac(const struct in6_addr *addr, struct ether_addr *mac);
+int mac_to_ipv6(const struct ether_addr *mac, alfred_addr *addr);
+int ipv6_to_mac(const alfred_addr *addr, struct ether_addr *mac);
+int ipv4_to_mac(const alfred_addr *addr, struct ether_addr *mac);
 int is_ipv6_eui64(const struct in6_addr *addr);
 
 #endif
diff --git a/client.c b/client.c
index 357fdb9..4102046 100644
--- a/client.c
+++ b/client.c
@@ -239,7 +239,7 @@  static int check_interface(const char *iface)
 		return -1;
 	}
 
-	sock = socket(PF_INET6, SOCK_DGRAM, 0);
+	sock = socket(PF_INET, SOCK_DGRAM, 0);
 	if (sock < 0) {
 		perror("can't open socket");
 		return -1;
diff --git a/main.c b/main.c
index 0d30ea4..c222f91 100644
--- a/main.c
+++ b/main.c
@@ -19,6 +19,7 @@ 
  *
  */
 
+#include <arpa/inet.h>
 #include <getopt.h>
 #include <signal.h>
 #include <stdio.h>
@@ -61,6 +62,7 @@  static void alfred_usage(void)
 	printf("                                      other masters\n");
 	printf("  -p, --sync-period [period]          set synchronization period, in seconds\n");
 	printf("                                      fractional seconds are supported (i.e. 0.2 = 5 Hz)\n");
+	printf("  -4                                  specify IPv4 multicast address and operate in IPv4 mode");
 	printf("\n");
 	printf("  -u, --unix-path [path]              path to unix socket used for client-server\n");
 	printf("                                      communication (default: \""ALFRED_SOCK_PATH_DEFAULT"\")\n");
@@ -196,6 +198,7 @@  static struct globals *alfred_init(int argc, char *argv[])
 	globals->mesh_iface = "bat0";
 	globals->unix_path = ALFRED_SOCK_PATH_DEFAULT;
 	globals->verbose = 0;
+  globals->ipv4mode = 0;
 	globals->update_command = NULL;
 	globals->sync_period.tv_sec = ALFRED_INTERVAL;
 	globals->sync_period.tv_nsec = 0;
@@ -204,7 +207,7 @@  static struct globals *alfred_init(int argc, char *argv[])
 
 	time_random_seed();
 
-	while ((opt = getopt_long(argc, argv, "ms:r:hi:b:vV:M:I:u:dc:p:", long_options,
+	while ((opt = getopt_long(argc, argv, "ms:r:hi:b:vV:M:I:u:dc:p:4:", long_options,
 				  &opt_ind)) != -1) {
 		switch (opt) {
 		case 'r':
@@ -277,6 +280,11 @@  static struct globals *alfred_init(int argc, char *argv[])
 			globals->sync_period.tv_nsec = (double) (sync_period - (int) sync_period) * 1e9;
 			printf(" ** Setting sync interval to: %.9f seconds (%ld.%09ld)\n", sync_period, globals->sync_period.tv_sec, globals->sync_period.tv_nsec);
 			break;
+		case '4':
+		  globals->ipv4mode = 1;
+		  inet_pton(AF_INET, optarg, &alfred_mcast.ipv4);
+		  printf(" ** IPv4 Multicast Mode: %x\n", alfred_mcast.ipv4.s_addr);
+		  break;
 		case 'h':
 		default:
 			alfred_usage();
diff --git a/man/alfred.8 b/man/alfred.8
index 5936e64..c4f0b96 100644
--- a/man/alfred.8
+++ b/man/alfred.8
@@ -123,6 +123,11 @@  as arguments.
 Specify alfred synchronization period, in seconds. If not specified, the default
 ALFRED_INTERVAL setting of 10 seconds will be used. Fractional seconds are
 supported.
+.TP
+\fB\-4\fP
+Specify the IPv4 multicast group address and enable IPv4 data-sharing (disabling
+IPv6 communication). This option is required on systems that do not support IPv6
+addressing.
 .
 .SH EXAMPLES
 Start an alfred server listening on bridge br0 (assuming that this bridge
diff --git a/netsock.c b/netsock.c
index 86db8a6..2e2d041 100644
--- a/netsock.c
+++ b/netsock.c
@@ -43,10 +43,12 @@ 
 #include "list.h"
 #include "hash.h"
 
-const struct in6_addr in6addr_localmcast = {{{ 0xff, 0x02, 0x00, 0x00,
-					       0x00, 0x00, 0x00, 0x00,
-					       0x00, 0x00, 0x00, 0x00,
-					       0x00, 0x00, 0x00, 0x01 } } };
+alfred_addr alfred_mcast = {
+    .ipv6={{{ 0xff, 0x02, 0x00, 0x00,
+              0x00, 0x00, 0x00, 0x00,
+              0x00, 0x00, 0x00, 0x00,
+              0x00, 0x00, 0x00, 0x01 } } }
+};
 
 static int server_compare(void *d1, void *d2)
 {
@@ -262,7 +264,7 @@  static int netsock_open(struct interface *interface)
 	memset(&sin6_mc, 0, sizeof(sin6_mc));
 	sin6_mc.sin6_port = htons(ALFRED_PORT);
 	sin6_mc.sin6_family = AF_INET6;
-	memcpy(&sin6_mc.sin6_addr, &in6addr_localmcast,
+	memcpy(&sin6_mc.sin6_addr, &alfred_mcast,
 	       sizeof(sin6_mc.sin6_addr));
 	sin6_mc.sin6_scope_id = interface->scope_id;
 
@@ -291,7 +293,7 @@  static int netsock_open(struct interface *interface)
 		goto err;
 	}
 
-	memcpy(&mreq.ipv6mr_multiaddr, &in6addr_localmcast,
+	memcpy(&mreq.ipv6mr_multiaddr, &alfred_mcast,
 	       sizeof(mreq.ipv6mr_multiaddr));
 	mreq.ipv6mr_interface = interface->scope_id;
 
@@ -335,6 +337,137 @@  err:
 	return -1;
 }
 
+static int netsock_open4(struct interface *interface)
+{
+  int sock;
+  int sock_mc;
+  struct sockaddr_in sin4, sin_mc;
+  struct ip_mreq mreq;
+  struct ifreq ifr;
+  int ret;
+
+  interface->netsock = -1;
+  interface->netsock_mcast = -1;
+
+  sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
+  if (sock  < 0) {
+    perror("ipv4: can't open socket");
+    return -1;
+  }
+
+  sock_mc = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
+  if (sock_mc  < 0) {
+    perror("ipv4: can't open mc socket");
+    return -1;
+  }
+
+  memset(&ifr, 0, sizeof(ifr));
+  strncpy(ifr.ifr_name, interface->interface, IFNAMSIZ);
+  ifr.ifr_name[IFNAMSIZ - 1] = '\0';
+  if (ioctl(sock_mc, SIOCGIFHWADDR, &ifr) == -1) {
+    perror("ipv4: can't get MAC address");
+    goto err;
+  }
+  memcpy(&interface->hwaddr, &ifr.ifr_hwaddr.sa_data, 6);
+
+  memset(&sin4, 0, sizeof(sin4));
+  sin4.sin_port = htons(ALFRED_PORT);
+  sin4.sin_family = AF_INET;
+  sin4.sin_addr.s_addr = INADDR_ANY;
+
+  memset(&sin_mc, 0, sizeof(sin_mc));
+  sin_mc.sin_port = htons(ALFRED_PORT);
+  sin_mc.sin_family = AF_INET;
+  memcpy(&sin_mc.sin_addr, &alfred_mcast, sizeof(sin_mc.sin_addr));
+
+  enable_raw_bind_capability(1);
+  if (setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, interface->interface,
+           strlen(interface->interface) + 1)) {
+    perror("ipv4: can't bind to device");
+    goto err;
+  }
+
+  if (setsockopt(sock_mc, SOL_SOCKET, SO_BINDTODEVICE,
+           interface->interface,
+           strlen(interface->interface) + 1)) {
+    perror("ipv4: can't bind to device");
+    goto err;
+  }
+  enable_raw_bind_capability(0);
+
+  ret = 1;
+  if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &ret, sizeof(ret)) < 0)
+  {
+    perror("ipv4: can't set reuse flag");
+    goto err;
+  }
+  if (setsockopt(sock_mc, SOL_SOCKET, SO_REUSEADDR, &ret, sizeof(ret)) < 0)
+  {
+    perror("ipv4: can't set mc reuse flag");
+    goto err;
+  }
+
+  if (bind(sock, (struct sockaddr *)&sin4, sizeof(sin4)) < 0) {
+    perror("ipv4: can't bind");
+    goto err;
+  }
+
+  if (bind(sock_mc, (struct sockaddr *)&sin_mc, sizeof(sin_mc)) < 0) {
+    perror("ipv4: can't bind mc");
+    goto err;
+  }
+
+  memcpy(&mreq.imr_multiaddr, &alfred_mcast.ipv4, sizeof(mreq.imr_multiaddr));
+
+  if (ioctl(sock_mc, SIOCGIFADDR, &ifr) < 0)
+  {
+    perror("ipv4: can't get IP address");
+    goto err;
+  }
+  mreq.imr_interface = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
+  memcpy(&interface->address.ipv4,
+      &((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr,
+      sizeof(struct in_addr));
+
+  if (setsockopt(sock_mc, IPPROTO_IP, IP_ADD_MEMBERSHIP,
+           &mreq, sizeof(mreq))) {
+    perror("ipv4: can't add multicast membership");
+    goto err;
+  }
+
+  ret = fcntl(sock, F_GETFL, 0);
+  if (ret < 0) {
+    perror("failed to get file status flags");
+    goto err;
+  }
+
+  ret = fcntl(sock, F_SETFL, ret | O_NONBLOCK);
+  if (ret < 0) {
+    perror("failed to set file status flags");
+    goto err;
+  }
+
+  ret = fcntl(sock_mc, F_GETFL, 0);
+  if (ret < 0) {
+    perror("ipv4: failed to get file status flags");
+    goto err;
+  }
+
+  ret = fcntl(sock_mc, F_SETFL, ret | O_NONBLOCK);
+  if (ret < 0) {
+    perror("ipv4: failed to set file status flags");
+    goto err;
+  }
+
+  interface->netsock = sock;
+  interface->netsock_mcast = sock_mc;
+
+  return 0;
+err:
+  close(sock_mc);
+  return -1;
+}
+
 int netsock_open_all(struct globals *globals)
 {
 	int num_socks = 0;
@@ -342,7 +475,11 @@  int netsock_open_all(struct globals *globals)
 	struct interface *interface;
 
 	list_for_each_entry(interface, &globals->interfaces, list) {
-		ret = netsock_open(interface);
+	  if (globals->ipv4mode)
+	    ret = netsock_open4(interface);
+	  else
+	    ret = netsock_open(interface);
+
 		if (ret >= 0)
 			num_socks++;
 	}
@@ -356,7 +493,12 @@  void netsock_reopen(struct globals *globals)
 
 	list_for_each_entry(interface, &globals->interfaces, list) {
 		if (interface->netsock < 0)
-			netsock_open(interface);
+		{
+	    if (globals->ipv4mode)
+	      netsock_open4(interface);
+	    else
+	      netsock_open(interface);
+		}
 	}
 }
 
@@ -430,7 +572,7 @@  int netsock_receive_packet(struct globals *globals, fd_set *fds)
 }
 
 int netsock_own_address(const struct globals *globals,
-			const struct in6_addr *address)
+			const alfred_addr *address)
 {
 	struct interface *interface;
 
diff --git a/recv.c b/recv.c
index bb8cc05..cc1ef5b 100644
--- a/recv.c
+++ b/recv.c
@@ -190,7 +190,7 @@  static int finish_alfred_transaction(struct globals *globals,
 }
 
 static int process_alfred_push_data(struct globals *globals,
-				    struct in6_addr *source,
+				    alfred_addr *source,
 				    struct alfred_push_data_v0 *push)
 {
 	int len;
@@ -200,7 +200,10 @@  static int process_alfred_push_data(struct globals *globals,
 	struct transaction_packet *transaction_packet;
 	int found;
 
-	ret = ipv6_to_mac(source, &mac);
+  if (globals->ipv4mode)
+    ret = ipv4_to_mac(source, &mac);
+  else
+    ret = ipv6_to_mac(source, &mac);
 	if (ret < 0)
 		goto err;
 
@@ -260,7 +263,7 @@  err:
 static int
 process_alfred_announce_master(struct globals *globals,
 			       struct interface *interface,
-			       struct in6_addr *source,
+			       alfred_addr *source,
 			       struct alfred_announce_master_v0 *announce)
 {
 	struct server *server;
@@ -271,7 +274,10 @@  process_alfred_announce_master(struct globals *globals,
 
 	len = ntohs(announce->header.length);
 
-	ret = ipv6_to_mac(source, &mac);
+	if (globals->ipv4mode)
+	  ret = ipv4_to_mac(source, &mac);
+	else
+	  ret = ipv6_to_mac(source, &mac);
 	if (ret < 0)
 		return -1;
 
@@ -316,7 +322,7 @@  process_alfred_announce_master(struct globals *globals,
 
 static int process_alfred_request(struct globals *globals,
 				  struct interface *interface,
-				  struct in6_addr *source,
+				  alfred_addr *source,
 				  struct alfred_request_v0 *request)
 {
 	int len;
@@ -336,7 +342,7 @@  static int process_alfred_request(struct globals *globals,
 }
 
 static int process_alfred_status_txend(struct globals *globals,
-				       struct in6_addr *source,
+				       alfred_addr *source,
 				       struct alfred_status_v0 *request)
 {
 	struct transaction_head search, *head;
@@ -351,7 +357,10 @@  static int process_alfred_status_txend(struct globals *globals,
 	if (len != (sizeof(*request) - sizeof(request->header)))
 		return -1;
 
-	ret = ipv6_to_mac(source, &mac);
+  if (globals->ipv4mode)
+    ret = ipv4_to_mac(source, &mac);
+  else
+    ret = ipv6_to_mac(source, &mac);
 	if (ret < 0)
 		return -1;
 
@@ -389,15 +398,28 @@  int recv_alfred_packet(struct globals *globals, struct interface *interface,
 	uint8_t buf[MAX_PAYLOAD];
 	ssize_t length;
 	struct alfred_tlv *packet;
-	struct sockaddr_in6 source;
+	struct sockaddr_in *source;
+	struct sockaddr_in source4;
+	struct sockaddr_in6 source6;
 	socklen_t sourcelen;
+	alfred_addr alfred_source;
 
 	if (interface->netsock < 0)
 		return -1;
 
-	sourcelen = sizeof(source);
+	if (globals->ipv4mode)
+	{
+    source = (struct sockaddr_in *) &source4;
+    sourcelen = sizeof(source4);
+	}
+	else
+	{
+    source = (struct sockaddr_in *) &source6;
+    sourcelen = sizeof(source6);
+	}
+
 	length = recvfrom(recv_sock, buf, sizeof(buf), 0,
-			  (struct sockaddr *)&source, &sourcelen);
+			  (struct sockaddr *) source, &sourcelen);
 	if (length <= 0) {
 		perror("read from network socket failed");
 		return -1;
@@ -405,12 +427,21 @@  int recv_alfred_packet(struct globals *globals, struct interface *interface,
 
 	packet = (struct alfred_tlv *)buf;
 
-	/* drop packets not sent over link-local ipv6 */
-	if (!is_ipv6_eui64(&source.sin6_addr))
-		return -1;
+  if (globals->ipv4mode)
+  {
+    memcpy(&alfred_source, &source4.sin_addr, sizeof(source4.sin_addr));
+  }
+  else
+  {
+    memcpy(&alfred_source, &source6.sin6_addr, sizeof(source6.sin6_addr));
+
+    /* drop packets not sent over link-local ipv6 */
+    if (!is_ipv6_eui64(&alfred_source.ipv6))
+      return -1;
+  }
 
 	/* drop packets from ourselves */
-	if (netsock_own_address(globals, &source.sin6_addr))
+	if (netsock_own_address(globals, &alfred_source))
 		return -1;
 
 	/* drop truncated packets */
@@ -424,20 +455,20 @@  int recv_alfred_packet(struct globals *globals, struct interface *interface,
 
 	switch (packet->type) {
 	case ALFRED_PUSH_DATA:
-		process_alfred_push_data(globals, &source.sin6_addr,
+		process_alfred_push_data(globals, &alfred_source,
 					 (struct alfred_push_data_v0 *)packet);
 		break;
 	case ALFRED_ANNOUNCE_MASTER:
 		process_alfred_announce_master(globals, interface,
-					       &source.sin6_addr,
+					       &alfred_source,
 					       (struct alfred_announce_master_v0 *)packet);
 		break;
 	case ALFRED_REQUEST:
-		process_alfred_request(globals, interface, &source.sin6_addr,
+		process_alfred_request(globals, interface, &alfred_source,
 				       (struct alfred_request_v0 *)packet);
 		break;
 	case ALFRED_STATUS_TXEND:
-		process_alfred_status_txend(globals, &source.sin6_addr,
+		process_alfred_status_txend(globals, &alfred_source,
 					    (struct alfred_status_v0 *)packet);
 		break;
 	default:
diff --git a/send.c b/send.c
index c7a2af2..81ff060 100644
--- a/send.c
+++ b/send.c
@@ -42,7 +42,7 @@  int announce_master(struct globals *globals)
 		announcement.header.version = ALFRED_VERSION;
 		announcement.header.length = htons(0);
 
-		send_alfred_packet(interface, &in6addr_localmcast,
+		send_alfred_packet(globals, interface, &alfred_mcast,
 				   &announcement, sizeof(announcement));
 	}
 
@@ -50,7 +50,7 @@  int announce_master(struct globals *globals)
 }
 
 int push_data(struct globals *globals, struct interface *interface,
-	      struct in6_addr *destination, enum data_source max_source_level,
+	      alfred_addr *destination, enum data_source max_source_level,
 	      int type_filter, uint16_t tx_id)
 {
 	struct hash_it_t *hashit = NULL;
@@ -90,7 +90,7 @@  int push_data(struct globals *globals, struct interface *interface,
 			tlv_length += sizeof(*push) - sizeof(push->header);
 			push->header.length = htons(tlv_length);
 			push->tx.seqno = htons(seqno++);
-			send_alfred_packet(interface, destination, push,
+			send_alfred_packet(globals, interface, destination, push,
 					   sizeof(*push) + total_length);
 			total_length = 0;
 		}
@@ -114,7 +114,7 @@  int push_data(struct globals *globals, struct interface *interface,
 		tlv_length += sizeof(*push) - sizeof(push->header);
 		push->header.length = htons(tlv_length);
 		push->tx.seqno = htons(seqno++);
-		send_alfred_packet(interface, destination, push,
+		send_alfred_packet(globals, interface, destination, push,
 				   sizeof(*push) + total_length);
 	}
 
@@ -128,7 +128,7 @@  int push_data(struct globals *globals, struct interface *interface,
 		status_end.tx.id = tx_id;
 		status_end.tx.seqno = htons(seqno);
 
-		send_alfred_packet(interface, destination, &status_end,
+		send_alfred_packet(globals, interface, destination, &status_end,
 				   sizeof(status_end));
 	}
 
@@ -170,24 +170,41 @@  int push_local_data(struct globals *globals)
 	return 0;
 }
 
-ssize_t send_alfred_packet(struct interface *interface,
-			   const struct in6_addr *dest, void *buf, int length)
+ssize_t send_alfred_packet(struct globals *globals, struct interface *interface,
+			   const alfred_addr *dest, void *buf, int length)
 {
 	ssize_t ret;
-	struct sockaddr_in6 dest_addr;
-
-	memset(&dest_addr, 0, sizeof(dest_addr));
-	dest_addr.sin6_family = AF_INET6;
-	dest_addr.sin6_port = htons(ALFRED_PORT);
-	dest_addr.sin6_scope_id = interface->scope_id;
-	memcpy(&dest_addr.sin6_addr, dest, sizeof(*dest));
+	struct sockaddr *dest_addr;
+	struct sockaddr_in6 dest_addr6;
+	struct sockaddr_in dest_addr4;
+	socklen_t slen;
+
+	if (globals->ipv4mode)
+	{
+    memset(&dest_addr4, 0, sizeof(dest_addr4));
+    dest_addr4.sin_family = AF_INET;
+    dest_addr4.sin_port = htons(ALFRED_PORT);
+    memcpy(&dest_addr4.sin_addr, &dest->ipv4, sizeof(dest->ipv4));
+
+    slen = sizeof(struct sockaddr_in);
+    dest_addr = (struct sockaddr *) &dest_addr4;
+	}
+  else
+  {
+    memset(&dest_addr6, 0, sizeof(dest_addr6));
+    dest_addr6.sin6_family = AF_INET6;
+    dest_addr6.sin6_port = htons(ALFRED_PORT);
+    dest_addr6.sin6_scope_id = interface->scope_id;
+    memcpy(&dest_addr6.sin6_addr, &dest->ipv6, sizeof(dest->ipv6));
+
+    slen = sizeof(struct sockaddr_in6);
+    dest_addr = (struct sockaddr *) &dest_addr6;
+  }
 
 	if (interface->netsock < 0)
 		return 0;
 
-	ret = sendto(interface->netsock, buf, length, 0,
-		     (struct sockaddr *)&dest_addr,
-		     sizeof(struct sockaddr_in6));
+	ret = sendto(interface->netsock, buf, length, 0, dest_addr, slen);
 	if (ret == -EPERM) {
 		perror("Error during sent");
 		close(interface->netsock);
diff --git a/server.c b/server.c
index f6e0c8a..337423a 100644
--- a/server.c
+++ b/server.c
@@ -218,7 +218,7 @@  static int purge_data(struct globals *globals)
 	return 0;
 }
 
-static void check_if_socket(struct interface *interface)
+static void check_if_socket(struct interface *interface, struct globals *globals)
 {
 	int sock;
 	struct ifreq ifr;
@@ -226,7 +226,7 @@  static void check_if_socket(struct interface *interface)
 	if (interface->netsock < 0)
 		return;
 
-	sock = socket(PF_INET6, SOCK_DGRAM, 0);
+	sock = socket(PF_INET, SOCK_DGRAM, 0);
 	if (sock < 0) {
 		perror("can't open socket");
 		return;
@@ -240,7 +240,7 @@  static void check_if_socket(struct interface *interface)
 		goto close;
 	}
 
-	if (interface->scope_id != (uint32_t)ifr.ifr_ifindex) {
+	if ((0 == globals->ipv4mode) && (interface->scope_id != (uint32_t)ifr.ifr_ifindex)) {
 		fprintf(stderr,
 			"iface index changed from %"PRIu32" to %d, closing netsock\n",
 			interface->scope_id, ifr.ifr_ifindex);
@@ -282,7 +282,7 @@  static void check_if_sockets(struct globals *globals)
 	globals->if_check = now;
 
 	list_for_each_entry(interface, &globals->interfaces, list)
-		check_if_socket(interface);
+		check_if_socket(interface, globals);
 }
 
 static void execute_update_command(struct globals *globals)
diff --git a/unix_sock.c b/unix_sock.c
index 0a819a2..4a8c6e0 100644
--- a/unix_sock.c
+++ b/unix_sock.c
@@ -258,7 +258,7 @@  static int unix_sock_req_data(struct globals *globals,
 	head->client_socket = client_sock;
 	head->requested_type = request->requested_type;
 
-	send_alfred_packet(interface, &globals->best_server->address,
+	send_alfred_packet(globals, interface, &globals->best_server->address,
 			   request, sizeof(*request));
 
 	return 0;