Commit cb6bf355 authored by YOSHIFUJI Hideaki / 吉藤英明's avatar YOSHIFUJI Hideaki / 吉藤英明 Committed by David S. Miller
Browse files

firewire net, ipv6: IPv6 over Firewire (RFC3146) support.

parent 6752c8db
Loading
Loading
Loading
Loading
+3 −3
Original line number Original line Diff line number Diff line
@@ -47,9 +47,9 @@ config FIREWIRE_NET
	tristate "IP networking over 1394"
	tristate "IP networking over 1394"
	depends on FIREWIRE && INET
	depends on FIREWIRE && INET
	help
	help
	  This enables IPv4 over IEEE 1394, providing IP connectivity with
	  This enables IPv4/IPv6 over IEEE 1394, providing IP connectivity
	  other implementations of RFC 2734 as found on several operating
	  with other implementations of RFC 2734/3146 as found on several
	  systems.  Multicast support is currently limited.
	  operating systems.  Multicast support is currently limited.


	  To compile this driver as a module, say M here:  The module will be
	  To compile this driver as a module, say M here:  The module will be
	  called firewire-net.
	  called firewire-net.
+74 −5
Original line number Original line Diff line number Diff line
/*
/*
 * IPv4 over IEEE 1394, per RFC 2734
 * IPv4 over IEEE 1394, per RFC 2734
 * IPv6 over IEEE 1394, per RFC 3146
 *
 *
 * Copyright (C) 2009 Jay Fenlason <fenlason@redhat.com>
 * Copyright (C) 2009 Jay Fenlason <fenlason@redhat.com>
 *
 *
@@ -46,6 +47,7 @@


#define IANA_SPECIFIER_ID		0x00005eU
#define IANA_SPECIFIER_ID		0x00005eU
#define RFC2734_SW_VERSION		0x000001U
#define RFC2734_SW_VERSION		0x000001U
#define RFC3146_SW_VERSION		0x000002U


#define IEEE1394_GASP_HDR_SIZE	8
#define IEEE1394_GASP_HDR_SIZE	8


@@ -505,6 +507,9 @@ static int fwnet_finish_incoming_packet(struct net_device *net,
	switch (ether_type) {
	switch (ether_type) {
	case ETH_P_ARP:
	case ETH_P_ARP:
	case ETH_P_IP:
	case ETH_P_IP:
#if IS_ENABLED(CONFIG_IPV6)
	case ETH_P_IPV6:
#endif
		break;
		break;
	default:
	default:
		goto err;
		goto err;
@@ -768,7 +773,12 @@ static void fwnet_receive_broadcast(struct fw_iso_context *context,
	ver = be32_to_cpu(buf_ptr[1]) & 0xffffff;
	ver = be32_to_cpu(buf_ptr[1]) & 0xffffff;
	source_node_id = be32_to_cpu(buf_ptr[0]) >> 16;
	source_node_id = be32_to_cpu(buf_ptr[0]) >> 16;


	if (specifier_id == IANA_SPECIFIER_ID && ver == RFC2734_SW_VERSION) {
	if (specifier_id == IANA_SPECIFIER_ID &&
	    (ver == RFC2734_SW_VERSION
#if IS_ENABLED(CONFIG_IPV6)
	     || ver == RFC3146_SW_VERSION
#endif
	    )) {
		buf_ptr += 2;
		buf_ptr += 2;
		length -= IEEE1394_GASP_HDR_SIZE;
		length -= IEEE1394_GASP_HDR_SIZE;
		fwnet_incoming_packet(dev, buf_ptr, length, source_node_id,
		fwnet_incoming_packet(dev, buf_ptr, length, source_node_id,
@@ -971,16 +981,27 @@ static int fwnet_send_packet(struct fwnet_packet_task *ptask)
		u8 *p;
		u8 *p;
		int generation;
		int generation;
		int node_id;
		int node_id;
		unsigned int sw_version;


		/* ptask->generation may not have been set yet */
		/* ptask->generation may not have been set yet */
		generation = dev->card->generation;
		generation = dev->card->generation;
		smp_rmb();
		smp_rmb();
		node_id = dev->card->node_id;
		node_id = dev->card->node_id;


		switch (ptask->skb->protocol) {
		default:
			sw_version = RFC2734_SW_VERSION;
			break;
#if IS_ENABLED(CONFIG_IPV6)
		case htons(ETH_P_IPV6):
			sw_version = RFC3146_SW_VERSION;
#endif
		}

		p = skb_push(ptask->skb, IEEE1394_GASP_HDR_SIZE);
		p = skb_push(ptask->skb, IEEE1394_GASP_HDR_SIZE);
		put_unaligned_be32(node_id << 16 | IANA_SPECIFIER_ID >> 8, p);
		put_unaligned_be32(node_id << 16 | IANA_SPECIFIER_ID >> 8, p);
		put_unaligned_be32((IANA_SPECIFIER_ID & 0xff) << 24
		put_unaligned_be32((IANA_SPECIFIER_ID & 0xff) << 24
						| RFC2734_SW_VERSION, &p[4]);
						| sw_version, &p[4]);


		/* We should not transmit if broadcast_channel.valid == 0. */
		/* We should not transmit if broadcast_channel.valid == 0. */
		fw_send_request(dev->card, &ptask->transaction,
		fw_send_request(dev->card, &ptask->transaction,
@@ -1248,6 +1269,9 @@ static netdev_tx_t fwnet_tx(struct sk_buff *skb, struct net_device *net)
	switch (proto) {
	switch (proto) {
	case htons(ETH_P_ARP):
	case htons(ETH_P_ARP):
	case htons(ETH_P_IP):
	case htons(ETH_P_IP):
#if IS_ENABLED(CONFIG_IPV6)
	case htons(ETH_P_IPV6):
#endif
		break;
		break;
	default:
	default:
		goto fail;
		goto fail;
@@ -1490,7 +1514,7 @@ static int fwnet_probe(struct device *_dev)
		goto out;
		goto out;


	list_add_tail(&dev->dev_link, &fwnet_device_list);
	list_add_tail(&dev->dev_link, &fwnet_device_list);
	dev_notice(&net->dev, "IPv4 over IEEE 1394 on card %s\n",
	dev_notice(&net->dev, "IP over IEEE 1394 on card %s\n",
		   dev_name(card->device));
		   dev_name(card->device));
 have_dev:
 have_dev:
	ret = fwnet_add_peer(dev, unit, device);
	ret = fwnet_add_peer(dev, unit, device);
@@ -1579,6 +1603,14 @@ static const struct ieee1394_device_id fwnet_id_table[] = {
		.specifier_id = IANA_SPECIFIER_ID,
		.specifier_id = IANA_SPECIFIER_ID,
		.version      = RFC2734_SW_VERSION,
		.version      = RFC2734_SW_VERSION,
	},
	},
#if IS_ENABLED(CONFIG_IPV6)
	{
		.match_flags  = IEEE1394_MATCH_SPECIFIER_ID |
				IEEE1394_MATCH_VERSION,
		.specifier_id = IANA_SPECIFIER_ID,
		.version      = RFC3146_SW_VERSION,
	},
#endif
	{ }
	{ }
};
};


@@ -1616,6 +1648,30 @@ static struct fw_descriptor rfc2374_unit_directory = {
	.data   = rfc2374_unit_directory_data
	.data   = rfc2374_unit_directory_data
};
};


#if IS_ENABLED(CONFIG_IPV6)
static const u32 rfc3146_unit_directory_data[] = {
	0x00040000,	/* directory_length		*/
	0x1200005e,	/* unit_specifier_id: IANA	*/
	0x81000003,	/* textual descriptor offset	*/
	0x13000002,	/* unit_sw_version: RFC 3146	*/
	0x81000005,	/* textual descriptor offset	*/
	0x00030000,	/* descriptor_length		*/
	0x00000000,	/* text				*/
	0x00000000,	/* minimal ASCII, en		*/
	0x49414e41,	/* I A N A			*/
	0x00030000,	/* descriptor_length		*/
	0x00000000,	/* text				*/
	0x00000000,	/* minimal ASCII, en		*/
	0x49507636,	/* I P v 6			*/
};

static struct fw_descriptor rfc3146_unit_directory = {
	.length = ARRAY_SIZE(rfc3146_unit_directory_data),
	.key    = (CSR_DIRECTORY | CSR_UNIT) << 24,
	.data   = rfc3146_unit_directory_data
};
#endif

static int __init fwnet_init(void)
static int __init fwnet_init(void)
{
{
	int err;
	int err;
@@ -1624,11 +1680,17 @@ static int __init fwnet_init(void)
	if (err)
	if (err)
		return err;
		return err;


#if IS_ENABLED(CONFIG_IPV6)
	err = fw_core_add_descriptor(&rfc3146_unit_directory);
	if (err)
		goto out;
#endif

	fwnet_packet_task_cache = kmem_cache_create("packet_task",
	fwnet_packet_task_cache = kmem_cache_create("packet_task",
			sizeof(struct fwnet_packet_task), 0, 0, NULL);
			sizeof(struct fwnet_packet_task), 0, 0, NULL);
	if (!fwnet_packet_task_cache) {
	if (!fwnet_packet_task_cache) {
		err = -ENOMEM;
		err = -ENOMEM;
		goto out;
		goto out2;
	}
	}


	err = driver_register(&fwnet_driver.driver);
	err = driver_register(&fwnet_driver.driver);
@@ -1636,7 +1698,11 @@ static int __init fwnet_init(void)
		return 0;
		return 0;


	kmem_cache_destroy(fwnet_packet_task_cache);
	kmem_cache_destroy(fwnet_packet_task_cache);
out2:
#if IS_ENABLED(CONFIG_IPV6)
	fw_core_remove_descriptor(&rfc3146_unit_directory);
out:
out:
#endif
	fw_core_remove_descriptor(&rfc2374_unit_directory);
	fw_core_remove_descriptor(&rfc2374_unit_directory);


	return err;
	return err;
@@ -1647,11 +1713,14 @@ static void __exit fwnet_cleanup(void)
{
{
	driver_unregister(&fwnet_driver.driver);
	driver_unregister(&fwnet_driver.driver);
	kmem_cache_destroy(fwnet_packet_task_cache);
	kmem_cache_destroy(fwnet_packet_task_cache);
#if IS_ENABLED(CONFIG_IPV6)
	fw_core_remove_descriptor(&rfc3146_unit_directory);
#endif
	fw_core_remove_descriptor(&rfc2374_unit_directory);
	fw_core_remove_descriptor(&rfc2374_unit_directory);
}
}
module_exit(fwnet_cleanup);
module_exit(fwnet_cleanup);


MODULE_AUTHOR("Jay Fenlason <fenlason@redhat.com>");
MODULE_AUTHOR("Jay Fenlason <fenlason@redhat.com>");
MODULE_DESCRIPTION("IPv4 over IEEE1394 as per RFC 2734");
MODULE_DESCRIPTION("IP over IEEE1394 as per RFC 2734/3146");
MODULE_LICENSE("GPL");
MODULE_LICENSE("GPL");
MODULE_DEVICE_TABLE(ieee1394, fwnet_id_table);
MODULE_DEVICE_TABLE(ieee1394, fwnet_id_table);
+19 −1
Original line number Original line Diff line number Diff line
@@ -70,6 +70,7 @@
#include <net/snmp.h>
#include <net/snmp.h>


#include <net/af_ieee802154.h>
#include <net/af_ieee802154.h>
#include <net/firewire.h>
#include <net/ipv6.h>
#include <net/ipv6.h>
#include <net/protocol.h>
#include <net/protocol.h>
#include <net/ndisc.h>
#include <net/ndisc.h>
@@ -1738,6 +1739,20 @@ static int addrconf_ifid_eui64(u8 *eui, struct net_device *dev)
	return 0;
	return 0;
}
}


static int addrconf_ifid_ieee1394(u8 *eui, struct net_device *dev)
{
	union fwnet_hwaddr *ha;

	if (dev->addr_len != FWNET_ALEN)
		return -1;

	ha = (union fwnet_hwaddr *)dev->dev_addr;

	memcpy(eui, &ha->uc.uniq_id, sizeof(ha->uc.uniq_id));
	eui[0] ^= 2;
	return 0;
}

static int addrconf_ifid_arcnet(u8 *eui, struct net_device *dev)
static int addrconf_ifid_arcnet(u8 *eui, struct net_device *dev)
{
{
	/* XXX: inherit EUI-64 from other interface -- yoshfuji */
	/* XXX: inherit EUI-64 from other interface -- yoshfuji */
@@ -1802,6 +1817,8 @@ static int ipv6_generate_eui64(u8 *eui, struct net_device *dev)
		return addrconf_ifid_gre(eui, dev);
		return addrconf_ifid_gre(eui, dev);
	case ARPHRD_IEEE802154:
	case ARPHRD_IEEE802154:
		return addrconf_ifid_eui64(eui, dev);
		return addrconf_ifid_eui64(eui, dev);
	case ARPHRD_IEEE1394:
		return addrconf_ifid_ieee1394(eui, dev);
	}
	}
	return -1;
	return -1;
}
}
@@ -2643,7 +2660,8 @@ static void addrconf_dev_config(struct net_device *dev)
	    (dev->type != ARPHRD_FDDI) &&
	    (dev->type != ARPHRD_FDDI) &&
	    (dev->type != ARPHRD_ARCNET) &&
	    (dev->type != ARPHRD_ARCNET) &&
	    (dev->type != ARPHRD_INFINIBAND) &&
	    (dev->type != ARPHRD_INFINIBAND) &&
	    (dev->type != ARPHRD_IEEE802154)) {
	    (dev->type != ARPHRD_IEEE802154) &&
	    (dev->type != ARPHRD_IEEE1394)) {
		/* Alas, we support only Ethernet autoconfiguration. */
		/* Alas, we support only Ethernet autoconfiguration. */
		return;
		return;
	}
	}