* [net-next-2.6 05/12] ixgbe: add LED blink code for x540
From: Jeff Kirsher @ 2011-04-27 9:34 UTC (permalink / raw)
To: davem; +Cc: Emil Tantilov, netdev, gospo, bphilips, Jeff Kirsher
In-Reply-To: <1303896891-16006-1-git-send-email-jeffrey.t.kirsher@intel.com>
From: Emil Tantilov <emil.s.tantilov@intel.com>
Implement blink_led_start and blink_led_stop functions for x540
using the MACC register.
Signed-off-by: Emil Tantilov <emil.s.tantilov@intel.com>
Tested-by: Evan Swanson <evan.swanson@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
---
drivers/net/ixgbe/ixgbe_x540.c | 64 ++++++++++++++++++++++++++++++++++++++-
1 files changed, 62 insertions(+), 2 deletions(-)
diff --git a/drivers/net/ixgbe/ixgbe_x540.c b/drivers/net/ixgbe/ixgbe_x540.c
index 932394f..75c6465 100644
--- a/drivers/net/ixgbe/ixgbe_x540.c
+++ b/drivers/net/ixgbe/ixgbe_x540.c
@@ -744,6 +744,66 @@ static void ixgbe_release_swfw_sync_semaphore(struct ixgbe_hw *hw)
IXGBE_WRITE_FLUSH(hw);
}
+/**
+ * ixgbe_blink_led_start_X540 - Blink LED based on index.
+ * @hw: pointer to hardware structure
+ * @index: led number to blink
+ *
+ * Devices that implement the version 2 interface:
+ * X540
+ **/
+static s32 ixgbe_blink_led_start_X540(struct ixgbe_hw *hw, u32 index)
+{
+ u32 macc_reg;
+ u32 ledctl_reg;
+
+ /*
+ * In order for the blink bit in the LED control register
+ * to work, link and speed must be forced in the MAC. We
+ * will reverse this when we stop the blinking.
+ */
+ macc_reg = IXGBE_READ_REG(hw, IXGBE_MACC);
+ macc_reg |= IXGBE_MACC_FLU | IXGBE_MACC_FSV_10G | IXGBE_MACC_FS;
+ IXGBE_WRITE_REG(hw, IXGBE_MACC, macc_reg);
+
+ /* Set the LED to LINK_UP + BLINK. */
+ ledctl_reg = IXGBE_READ_REG(hw, IXGBE_LEDCTL);
+ ledctl_reg &= ~IXGBE_LED_MODE_MASK(index);
+ ledctl_reg |= IXGBE_LED_BLINK(index);
+ IXGBE_WRITE_REG(hw, IXGBE_LEDCTL, ledctl_reg);
+ IXGBE_WRITE_FLUSH(hw);
+
+ return 0;
+}
+
+/**
+ * ixgbe_blink_led_stop_X540 - Stop blinking LED based on index.
+ * @hw: pointer to hardware structure
+ * @index: led number to stop blinking
+ *
+ * Devices that implement the version 2 interface:
+ * X540
+ **/
+static s32 ixgbe_blink_led_stop_X540(struct ixgbe_hw *hw, u32 index)
+{
+ u32 macc_reg;
+ u32 ledctl_reg;
+
+ /* Restore the LED to its default value. */
+ ledctl_reg = IXGBE_READ_REG(hw, IXGBE_LEDCTL);
+ ledctl_reg &= ~IXGBE_LED_MODE_MASK(index);
+ ledctl_reg |= IXGBE_LED_LINK_ACTIVE << IXGBE_LED_MODE_SHIFT(index);
+ ledctl_reg &= ~IXGBE_LED_BLINK(index);
+ IXGBE_WRITE_REG(hw, IXGBE_LEDCTL, ledctl_reg);
+
+ /* Unforce link and speed in the MAC. */
+ macc_reg = IXGBE_READ_REG(hw, IXGBE_MACC);
+ macc_reg &= ~(IXGBE_MACC_FLU | IXGBE_MACC_FSV_10G | IXGBE_MACC_FS);
+ IXGBE_WRITE_REG(hw, IXGBE_MACC, macc_reg);
+ IXGBE_WRITE_FLUSH(hw);
+
+ return 0;
+}
static struct ixgbe_mac_operations mac_ops_X540 = {
.init_hw = &ixgbe_init_hw_generic,
.reset_hw = &ixgbe_reset_hw_X540,
@@ -767,8 +827,8 @@ static struct ixgbe_mac_operations mac_ops_X540 = {
.get_link_capabilities = &ixgbe_get_copper_link_capabilities_generic,
.led_on = &ixgbe_led_on_generic,
.led_off = &ixgbe_led_off_generic,
- .blink_led_start = &ixgbe_blink_led_start_generic,
- .blink_led_stop = &ixgbe_blink_led_stop_generic,
+ .blink_led_start = &ixgbe_blink_led_start_X540,
+ .blink_led_stop = &ixgbe_blink_led_stop_X540,
.set_rar = &ixgbe_set_rar_generic,
.clear_rar = &ixgbe_clear_rar_generic,
.set_vmdq = &ixgbe_set_vmdq_generic,
--
1.7.4.4
^ permalink raw reply related
* [net-next-2.6 04/12] ixgbe: add support for 64k EEPROM for 82599
From: Jeff Kirsher @ 2011-04-27 9:34 UTC (permalink / raw)
To: davem; +Cc: Emil Tantilov, netdev, gospo, bphilips, Jeff Kirsher
In-Reply-To: <1303896891-16006-1-git-send-email-jeffrey.t.kirsher@intel.com>
From: Emil Tantilov <emil.s.tantilov@intel.com>
82599 supports up to 32k EEPROM addressing via EERD register. If we
wish to address larger EEPROM this have to be done via serial interface.
This patch adds function ixgbe_read_eeprom_82599 which selects the best
method to read the EEPROM.
Signed-off-by: Emil Tantilov <emil.s.tantilov@intel.com>
Tested-by: Evan Swanson <evan.swanson@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
---
drivers/net/ixgbe/ixgbe_82599.c | 31 ++++++++++++++++++++++++++++++-
1 files changed, 30 insertions(+), 1 deletions(-)
diff --git a/drivers/net/ixgbe/ixgbe_82599.c b/drivers/net/ixgbe/ixgbe_82599.c
index b341ed8..d521baf 100644
--- a/drivers/net/ixgbe/ixgbe_82599.c
+++ b/drivers/net/ixgbe/ixgbe_82599.c
@@ -2064,6 +2064,35 @@ out:
return lesm_enabled;
}
+/**
+ * ixgbe_read_eeprom_82599 - Read EEPROM word using
+ * fastest available method
+ *
+ * @hw: pointer to hardware structure
+ * @offset: offset of word in the EEPROM to read
+ * @data: word read from the EEPROM
+ *
+ * Reads a 16 bit word from the EEPROM
+ **/
+static s32 ixgbe_read_eeprom_82599(struct ixgbe_hw *hw,
+ u16 offset, u16 *data)
+{
+ struct ixgbe_eeprom_info *eeprom = &hw->eeprom;
+ s32 ret_val = IXGBE_ERR_CONFIG;
+
+ /*
+ * If EEPROM is detected and can be addressed using 14 bits,
+ * use EERD otherwise use bit bang
+ */
+ if ((eeprom->type == ixgbe_eeprom_spi) &&
+ (offset <= IXGBE_EERD_MAX_ADDR))
+ ret_val = ixgbe_read_eerd_generic(hw, offset, data);
+ else
+ ret_val = ixgbe_read_eeprom_bit_bang_generic(hw, offset, data);
+
+ return ret_val;
+}
+
static struct ixgbe_mac_operations mac_ops_82599 = {
.init_hw = &ixgbe_init_hw_generic,
.reset_hw = &ixgbe_reset_hw_82599,
@@ -2110,7 +2139,7 @@ static struct ixgbe_mac_operations mac_ops_82599 = {
static struct ixgbe_eeprom_operations eeprom_ops_82599 = {
.init_params = &ixgbe_init_eeprom_params_generic,
- .read = &ixgbe_read_eerd_generic,
+ .read = &ixgbe_read_eeprom_82599,
.write = &ixgbe_write_eeprom_generic,
.calc_checksum = &ixgbe_calc_eeprom_checksum_generic,
.validate_checksum = &ixgbe_validate_eeprom_checksum_generic,
--
1.7.4.4
^ permalink raw reply related
* [net-next-2.6 08/12] ixgbe: do not clear FCoE DDP error status for received ABTS
From: Jeff Kirsher @ 2011-04-27 9:34 UTC (permalink / raw)
To: davem; +Cc: Yi Zou, netdev, gospo, bphilips, Jeff Kirsher
In-Reply-To: <1303896891-16006-1-git-send-email-jeffrey.t.kirsher@intel.com>
From: Yi Zou <yi.zou@intel.com>
The ddp->err is initialized to be 1 to make sure outstanding DDP context is
guaranteed to be invalidated when HW is not auto-invalidating it. However,
in case of receiving ABTS response for a DDPed I/O, the ddp->err was cleared,
bypassing the invalidating of the DDP context from upper protocol stack when
ixgbe_fcoe_ddp_put() is called. This bug is fixed here by updating the error
only when FCP_RSP is received.
Signed-off-by: Yi Zou <yi.zou@intel.com>
Tested-by: Ross Brattain <ross.b.brattain@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
---
drivers/net/ixgbe/ixgbe_fcoe.c | 4 ++--
1 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/net/ixgbe/ixgbe_fcoe.c b/drivers/net/ixgbe/ixgbe_fcoe.c
index dba7d77..0592072 100644
--- a/drivers/net/ixgbe/ixgbe_fcoe.c
+++ b/drivers/net/ixgbe/ixgbe_fcoe.c
@@ -416,8 +416,7 @@ int ixgbe_fcoe_ddp(struct ixgbe_adapter *adapter,
if (!ddp->udl)
goto ddp_out;
- ddp->err = (fcerr | fceofe);
- if (ddp->err)
+ if (fcerr | fceofe)
goto ddp_out;
fcstat = (sterr & IXGBE_RXDADV_STAT_FCSTAT);
@@ -428,6 +427,7 @@ int ixgbe_fcoe_ddp(struct ixgbe_adapter *adapter,
if (fcstat == IXGBE_RXDADV_STAT_FCSTAT_FCPRSP) {
pci_unmap_sg(adapter->pdev, ddp->sgl,
ddp->sgc, DMA_FROM_DEVICE);
+ ddp->err = (fcerr | fceofe);
ddp->sgl = NULL;
ddp->sgc = 0;
}
--
1.7.4.4
^ permalink raw reply related
* [net-next-2.6 02/12] ixgbe: explicitly disable 100H for x540
From: Jeff Kirsher @ 2011-04-27 9:34 UTC (permalink / raw)
To: davem; +Cc: Emil Tantilov, netdev, gospo, bphilips, Jeff Kirsher
In-Reply-To: <1303896891-16006-1-git-send-email-jeffrey.t.kirsher@intel.com>
From: Emil Tantilov <emil.s.tantilov@intel.com>
100H is not supported on this HW, but the bit is set on the PHY.
This can result in link at 100F when advertising only 1000F.
Signed-off-by: Emil Tantilov <emil.s.tantilov@intel.com>
Tested-by: Evan Swanson <evan.swanson@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
---
drivers/net/ixgbe/ixgbe_phy.c | 3 ++-
1 files changed, 2 insertions(+), 1 deletions(-)
diff --git a/drivers/net/ixgbe/ixgbe_phy.c b/drivers/net/ixgbe/ixgbe_phy.c
index fd381ea..edcaaeb 100644
--- a/drivers/net/ixgbe/ixgbe_phy.c
+++ b/drivers/net/ixgbe/ixgbe_phy.c
@@ -657,7 +657,8 @@ s32 ixgbe_setup_phy_link_tnx(struct ixgbe_hw *hw)
MDIO_MMD_AN,
&autoneg_reg);
- autoneg_reg &= ~ADVERTISE_100FULL;
+ autoneg_reg &= ~(ADVERTISE_100FULL |
+ ADVERTISE_100HALF);
if (hw->phy.autoneg_advertised & IXGBE_LINK_SPEED_100_FULL)
autoneg_reg |= ADVERTISE_100FULL;
--
1.7.4.4
^ permalink raw reply related
* [net-next-2.6 01/12] e1000e: implement ethtool set_phys_id
From: Jeff Kirsher @ 2011-04-27 9:34 UTC (permalink / raw)
To: davem; +Cc: Bruce Allan, netdev, gospo, bphilips, Stephen Hemminger,
Jeff Kirsher
In-Reply-To: <1303896891-16006-1-git-send-email-jeffrey.t.kirsher@intel.com>
From: Bruce Allan <bruce.w.allan@intel.com>
Based on a patch from Stephen Hemminger <shemminger@vyatta.com>.
The new ethtool set_phys_id takes over controlling the LED for
identifying boards. This fixes the lockout during that period.
For this device lots of extra infrastructure can also be removed by
using set_phys_id.
v2: - return blink frequency for parts that do not support blink in h/w
- add blink_led function pointers for devices that do support blink
in h/w to cleanup the test for this functionality
Signed-off-by: Bruce Allan <bruce.w.allan@intel.com>
Cc: Stephen Hemminger <shemminger@vyatta.com>
Tested-by: Aaron Brown <aaron.f.brown@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
---
drivers/net/e1000e/82571.c | 2 +
drivers/net/e1000e/e1000.h | 6 +---
drivers/net/e1000e/es2lan.c | 1 +
drivers/net/e1000e/ethtool.c | 71 ++++++++++++-----------------------------
drivers/net/e1000e/hw.h | 1 +
drivers/net/e1000e/ich8lan.c | 4 ++
drivers/net/e1000e/lib.c | 4 +-
drivers/net/e1000e/netdev.c | 2 -
8 files changed, 32 insertions(+), 59 deletions(-)
diff --git a/drivers/net/e1000e/82571.c b/drivers/net/e1000e/82571.c
index ae07d37..8295f21 100644
--- a/drivers/net/e1000e/82571.c
+++ b/drivers/net/e1000e/82571.c
@@ -300,6 +300,7 @@ static s32 e1000_init_mac_params_82571(struct e1000_adapter *adapter)
func->set_lan_id = e1000_set_lan_id_single_port;
func->check_mng_mode = e1000e_check_mng_mode_generic;
func->led_on = e1000e_led_on_generic;
+ func->blink_led = e1000e_blink_led_generic;
/* FWSM register */
mac->has_fwsm = true;
@@ -320,6 +321,7 @@ static s32 e1000_init_mac_params_82571(struct e1000_adapter *adapter)
default:
func->check_mng_mode = e1000e_check_mng_mode_generic;
func->led_on = e1000e_led_on_generic;
+ func->blink_led = e1000e_blink_led_generic;
/* FWSM register */
mac->has_fwsm = true;
diff --git a/drivers/net/e1000e/e1000.h b/drivers/net/e1000e/e1000.h
index 3be5478..9549879 100644
--- a/drivers/net/e1000e/e1000.h
+++ b/drivers/net/e1000e/e1000.h
@@ -391,13 +391,10 @@ struct e1000_adapter {
bool fc_autoneg;
- unsigned long led_status;
-
unsigned int flags;
unsigned int flags2;
struct work_struct downshift_task;
struct work_struct update_phy_task;
- struct work_struct led_blink_task;
struct work_struct print_hang_task;
bool idle_check;
@@ -487,7 +484,6 @@ extern const char e1000e_driver_version[];
extern void e1000e_check_options(struct e1000_adapter *adapter);
extern void e1000e_set_ethtool_ops(struct net_device *netdev);
-extern void e1000e_led_blink_task(struct work_struct *work);
extern int e1000e_up(struct e1000_adapter *adapter);
extern void e1000e_down(struct e1000_adapter *adapter);
@@ -575,7 +571,7 @@ extern s32 e1000e_valid_led_default(struct e1000_hw *hw, u16 *data);
extern void e1000e_config_collision_dist(struct e1000_hw *hw);
extern s32 e1000e_config_fc_after_link_up(struct e1000_hw *hw);
extern s32 e1000e_force_mac_fc(struct e1000_hw *hw);
-extern s32 e1000e_blink_led(struct e1000_hw *hw);
+extern s32 e1000e_blink_led_generic(struct e1000_hw *hw);
extern void e1000_write_vfta_generic(struct e1000_hw *hw, u32 offset, u32 value);
extern s32 e1000_check_alt_mac_addr_generic(struct e1000_hw *hw);
extern void e1000e_reset_adaptive(struct e1000_hw *hw);
diff --git a/drivers/net/e1000e/es2lan.c b/drivers/net/e1000e/es2lan.c
index 0279695..f4bbeb2 100644
--- a/drivers/net/e1000e/es2lan.c
+++ b/drivers/net/e1000e/es2lan.c
@@ -1434,6 +1434,7 @@ static void e1000_clear_hw_cntrs_80003es2lan(struct e1000_hw *hw)
static struct e1000_mac_operations es2_mac_ops = {
.read_mac_addr = e1000_read_mac_addr_80003es2lan,
.id_led_init = e1000e_id_led_init,
+ .blink_led = e1000e_blink_led_generic,
.check_mng_mode = e1000e_check_mng_mode_generic,
/* check_for_link dependent on media type */
.cleanup_led = e1000e_cleanup_led_generic,
diff --git a/drivers/net/e1000e/ethtool.c b/drivers/net/e1000e/ethtool.c
index a31d280..1d7bf40 100644
--- a/drivers/net/e1000e/ethtool.c
+++ b/drivers/net/e1000e/ethtool.c
@@ -1851,64 +1851,35 @@ static int e1000_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol)
return 0;
}
-/* toggle LED 4 times per second = 2 "blinks" per second */
-#define E1000_ID_INTERVAL (HZ/4)
-
-/* bit defines for adapter->led_status */
-#define E1000_LED_ON 0
-
-void e1000e_led_blink_task(struct work_struct *work)
-{
- struct e1000_adapter *adapter = container_of(work,
- struct e1000_adapter, led_blink_task);
-
- if (test_and_change_bit(E1000_LED_ON, &adapter->led_status))
- adapter->hw.mac.ops.led_off(&adapter->hw);
- else
- adapter->hw.mac.ops.led_on(&adapter->hw);
-}
-
-static void e1000_led_blink_callback(unsigned long data)
-{
- struct e1000_adapter *adapter = (struct e1000_adapter *) data;
-
- schedule_work(&adapter->led_blink_task);
- mod_timer(&adapter->blink_timer, jiffies + E1000_ID_INTERVAL);
-}
-
-static int e1000_phys_id(struct net_device *netdev, u32 data)
+static int e1000_set_phys_id(struct net_device *netdev,
+ enum ethtool_phys_id_state state)
{
struct e1000_adapter *adapter = netdev_priv(netdev);
struct e1000_hw *hw = &adapter->hw;
- if (!data)
- data = INT_MAX;
+ switch (state) {
+ case ETHTOOL_ID_ACTIVE:
+ if (!hw->mac.ops.blink_led)
+ return 2; /* cycle on/off twice per second */
- if ((hw->phy.type == e1000_phy_ife) ||
- (hw->mac.type == e1000_pchlan) ||
- (hw->mac.type == e1000_pch2lan) ||
- (hw->mac.type == e1000_82583) ||
- (hw->mac.type == e1000_82574)) {
- if (!adapter->blink_timer.function) {
- init_timer(&adapter->blink_timer);
- adapter->blink_timer.function =
- e1000_led_blink_callback;
- adapter->blink_timer.data = (unsigned long) adapter;
- }
- mod_timer(&adapter->blink_timer, jiffies);
- msleep_interruptible(data * 1000);
- del_timer_sync(&adapter->blink_timer);
+ hw->mac.ops.blink_led(hw);
+ break;
+
+ case ETHTOOL_ID_INACTIVE:
if (hw->phy.type == e1000_phy_ife)
e1e_wphy(hw, IFE_PHY_SPECIAL_CONTROL_LED, 0);
- } else {
- e1000e_blink_led(hw);
- msleep_interruptible(data * 1000);
- }
+ hw->mac.ops.led_off(hw);
+ hw->mac.ops.cleanup_led(hw);
+ break;
- hw->mac.ops.led_off(hw);
- clear_bit(E1000_LED_ON, &adapter->led_status);
- hw->mac.ops.cleanup_led(hw);
+ case ETHTOOL_ID_ON:
+ adapter->hw.mac.ops.led_on(&adapter->hw);
+ break;
+ case ETHTOOL_ID_OFF:
+ adapter->hw.mac.ops.led_off(&adapter->hw);
+ break;
+ }
return 0;
}
@@ -2074,7 +2045,7 @@ static const struct ethtool_ops e1000_ethtool_ops = {
.set_tso = e1000_set_tso,
.self_test = e1000_diag_test,
.get_strings = e1000_get_strings,
- .phys_id = e1000_phys_id,
+ .set_phys_id = e1000_set_phys_id,
.get_ethtool_stats = e1000_get_ethtool_stats,
.get_sset_count = e1000e_get_sset_count,
.get_coalesce = e1000_get_coalesce,
diff --git a/drivers/net/e1000e/hw.h b/drivers/net/e1000e/hw.h
index 307e1ec..6c2fa83 100644
--- a/drivers/net/e1000e/hw.h
+++ b/drivers/net/e1000e/hw.h
@@ -756,6 +756,7 @@ struct e1000_host_mng_command_info {
/* Function pointers and static data for the MAC. */
struct e1000_mac_operations {
s32 (*id_led_init)(struct e1000_hw *);
+ s32 (*blink_led)(struct e1000_hw *);
bool (*check_mng_mode)(struct e1000_hw *);
s32 (*check_for_link)(struct e1000_hw *);
s32 (*cleanup_led)(struct e1000_hw *);
diff --git a/drivers/net/e1000e/ich8lan.c b/drivers/net/e1000e/ich8lan.c
index 06ff884..3369d1f 100644
--- a/drivers/net/e1000e/ich8lan.c
+++ b/drivers/net/e1000e/ich8lan.c
@@ -564,6 +564,8 @@ static s32 e1000_init_mac_params_ich8lan(struct e1000_adapter *adapter)
mac->ops.check_mng_mode = e1000_check_mng_mode_ich8lan;
/* ID LED init */
mac->ops.id_led_init = e1000e_id_led_init;
+ /* blink LED */
+ mac->ops.blink_led = e1000e_blink_led_generic;
/* setup LED */
mac->ops.setup_led = e1000e_setup_led_generic;
/* cleanup LED */
@@ -767,6 +769,8 @@ static s32 e1000_get_variants_ich8lan(struct e1000_adapter *adapter)
(!(er32(CTRL_EXT) & E1000_CTRL_EXT_LSECCK)))) {
adapter->flags &= ~FLAG_HAS_JUMBO_FRAMES;
adapter->max_hw_frame_size = ETH_FRAME_LEN + ETH_FCS_LEN;
+
+ hw->mac.ops.blink_led = NULL;
}
if ((adapter->hw.mac.type == e1000_ich8lan) &&
diff --git a/drivers/net/e1000e/lib.c b/drivers/net/e1000e/lib.c
index 30ef8fa..6432dda 100644
--- a/drivers/net/e1000e/lib.c
+++ b/drivers/net/e1000e/lib.c
@@ -1530,12 +1530,12 @@ s32 e1000e_cleanup_led_generic(struct e1000_hw *hw)
}
/**
- * e1000e_blink_led - Blink LED
+ * e1000e_blink_led_generic - Blink LED
* @hw: pointer to the HW structure
*
* Blink the LEDs which are set to be on.
**/
-s32 e1000e_blink_led(struct e1000_hw *hw)
+s32 e1000e_blink_led_generic(struct e1000_hw *hw)
{
u32 ledctl_blink = 0;
u32 i;
diff --git a/drivers/net/e1000e/netdev.c b/drivers/net/e1000e/netdev.c
index 4deb67d..0939040 100644
--- a/drivers/net/e1000e/netdev.c
+++ b/drivers/net/e1000e/netdev.c
@@ -6020,7 +6020,6 @@ static int __devinit e1000_probe(struct pci_dev *pdev,
INIT_WORK(&adapter->downshift_task, e1000e_downshift_workaround);
INIT_WORK(&adapter->update_phy_task, e1000e_update_phy_task);
INIT_WORK(&adapter->print_hang_task, e1000_print_hw_hang);
- INIT_WORK(&adapter->led_blink_task, e1000e_led_blink_task);
/* Initialize link parameters. User can change them with ethtool */
adapter->hw.mac.autoneg = 1;
@@ -6153,7 +6152,6 @@ static void __devexit e1000_remove(struct pci_dev *pdev)
cancel_work_sync(&adapter->watchdog_task);
cancel_work_sync(&adapter->downshift_task);
cancel_work_sync(&adapter->update_phy_task);
- cancel_work_sync(&adapter->led_blink_task);
cancel_work_sync(&adapter->print_hang_task);
if (!(netdev->flags & IFF_UP))
--
1.7.4.4
^ permalink raw reply related
* [net-next-2.6 00/12][pull request] Intel Wired LAN Driver Update
From: Jeff Kirsher @ 2011-04-27 9:34 UTC (permalink / raw)
To: davem; +Cc: Jeff Kirsher, netdev, gospo, bphilips
The following series contains updates to e1000e and ixgbe.
- e1000e: convert to new set_phys_id interface
- ixgbe: several cleanups and fixes, as well as the conversion
to the new set_phys_id interface and driver version bump.
The following are changes since commit 2bd93d7af1581d40e3c4b25242472661cb7c637a:
Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/davem/net-2.6
and are available in the git repository at:
master.kernel.org:/pub/scm/linux/kernel/git/jkirsher/net-next-2.6 master
Alexander Duyck (1):
ixgbe: remove ntuple display support
Bruce Allan (1):
e1000e: implement ethtool set_phys_id
Don Skidmore (3):
ixgbe: enable SCTP checksum offload for X540
ixgbe: Bump version
ixgbe: fix X540 ethtool loopback test.
Emil Tantilov (5):
ixgbe: explicitly disable 100H for x540
ixgbe: register defines cleanup
ixgbe: add support for 64k EEPROM for 82599
ixgbe: add LED blink code for x540
ixgbe: convert to ethtool set_phys_id
John Fastabend (1):
ixgbe: fix static functions
Yi Zou (1):
ixgbe: do not clear FCoE DDP error status for received ABTS
drivers/net/e1000e/82571.c | 2 +
drivers/net/e1000e/e1000.h | 6 +-
drivers/net/e1000e/es2lan.c | 1 +
drivers/net/e1000e/ethtool.c | 71 ++++---------
drivers/net/e1000e/hw.h | 1 +
drivers/net/e1000e/ich8lan.c | 4 +
drivers/net/e1000e/lib.c | 4 +-
drivers/net/e1000e/netdev.c | 2 -
drivers/net/ixgbe/ixgbe.h | 1 +
drivers/net/ixgbe/ixgbe_82599.c | 33 ++++++-
drivers/net/ixgbe/ixgbe_dcb_82598.c | 2 +-
drivers/net/ixgbe/ixgbe_ethtool.c | 39 +++++---
drivers/net/ixgbe/ixgbe_fcoe.c | 4 +-
drivers/net/ixgbe/ixgbe_main.c | 19 ++--
drivers/net/ixgbe/ixgbe_mbx.h | 3 -
drivers/net/ixgbe/ixgbe_phy.c | 3 +-
drivers/net/ixgbe/ixgbe_sriov.c | 2 +-
drivers/net/ixgbe/ixgbe_type.h | 189 +++++++++++++++++++++++------------
drivers/net/ixgbe/ixgbe_x540.c | 64 ++++++++++++-
19 files changed, 292 insertions(+), 158 deletions(-)
--
1.7.4.4
^ permalink raw reply
* [PATCH] Applying inappropriate ioctl operation on socket should return ENOTTY
From: Lifeng Sun @ 2011-04-27 9:32 UTC (permalink / raw)
To: netdev; +Cc: davem, Lifeng Sun
In-Reply-To: <1303883910.2699.53.camel@edumazet-laptop>
ioctl() calls against a socket with an inappropriate ioctl operation
are incorrectly returning EINVAL rather than ENOTTY:
[ENOTTY]
Inappropriate I/O control operation.
BugLink: https://bugzilla.kernel.org/show_bug.cgi?id=33992
This bug is not limited to socket, it also occurs in a lot of, maybe
some hundred, other ioctl operations, while in the patch I only fixed
about a dozen of additional ones in pipe, fifo and character device
drivers.
Signed-off-by: Lifeng Sun <lifongsun@gmail.com>
---
drivers/char/applicom.c | 2 +-
drivers/char/dtlk.c | 2 +-
drivers/char/generic_nvram.c | 2 +-
drivers/char/genrtc.c | 2 +-
drivers/char/hpet.c | 2 +-
drivers/char/i8k.c | 2 +-
drivers/char/ipmi/ipmi_devintf.c | 2 +-
drivers/char/lp.c | 2 +-
drivers/char/nwflash.c | 2 +-
drivers/char/ppdev.c | 2 +-
drivers/char/random.c | 2 +-
drivers/char/raw.c | 4 ++--
drivers/char/viotape.c | 2 +-
fs/pipe.c | 2 +-
net/core/dev.c | 6 +++---
15 files changed, 18 insertions(+), 18 deletions(-)
diff --git a/drivers/char/applicom.c b/drivers/char/applicom.c
index 25373df..50c09e4 100644
--- a/drivers/char/applicom.c
+++ b/drivers/char/applicom.c
@@ -838,6 +838,6 @@ static long ac_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
Dummy = readb(apbs[IndexCard].RamIO + VERS);
kfree(adgl);
mutex_unlock(&ac_mutex);
- return 0;
+ return ret;
}
diff --git a/drivers/char/dtlk.c b/drivers/char/dtlk.c
index 85156dd..2d116d5 100644
--- a/drivers/char/dtlk.c
+++ b/drivers/char/dtlk.c
@@ -289,7 +289,7 @@ static long dtlk_ioctl(struct file *file,
return put_user(portval, argp);
default:
- return -EINVAL;
+ return -ENOTTY;
}
}
diff --git a/drivers/char/generic_nvram.c b/drivers/char/generic_nvram.c
index 0e941b5..95278e9 100644
--- a/drivers/char/generic_nvram.c
+++ b/drivers/char/generic_nvram.c
@@ -111,7 +111,7 @@ static int nvram_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
nvram_sync();
break;
default:
- return -EINVAL;
+ return -ENOTTY;
}
return 0;
diff --git a/drivers/char/genrtc.c b/drivers/char/genrtc.c
index f773a9d..6f4c3da 100644
--- a/drivers/char/genrtc.c
+++ b/drivers/char/genrtc.c
@@ -330,7 +330,7 @@ static int gen_rtc_ioctl(struct file *file,
}
}
- return -EINVAL;
+ return -ENOTTY;
}
static long gen_rtc_unlocked_ioctl(struct file *file, unsigned int cmd,
diff --git a/drivers/char/hpet.c b/drivers/char/hpet.c
index 7066e80..720de66 100644
--- a/drivers/char/hpet.c
+++ b/drivers/char/hpet.c
@@ -575,7 +575,7 @@ hpet_ioctl_common(struct hpet_dev *devp, int cmd, unsigned long arg,
case HPET_IE_ON:
return hpet_ioctl_ieon(devp);
default:
- return -EINVAL;
+ return -ENOTTY;
}
err = 0;
diff --git a/drivers/char/i8k.c b/drivers/char/i8k.c
index d72433f..4ba9b9f 100644
--- a/drivers/char/i8k.c
+++ b/drivers/char/i8k.c
@@ -370,7 +370,7 @@ i8k_ioctl_unlocked(struct file *fp, unsigned int cmd, unsigned long arg)
break;
default:
- return -EINVAL;
+ return -ENOTTY;
}
if (val < 0)
diff --git a/drivers/char/ipmi/ipmi_devintf.c b/drivers/char/ipmi/ipmi_devintf.c
index 2aa3977..bc8af5a 100644
--- a/drivers/char/ipmi/ipmi_devintf.c
+++ b/drivers/char/ipmi/ipmi_devintf.c
@@ -232,7 +232,7 @@ static int ipmi_ioctl(struct file *file,
unsigned int cmd,
unsigned long data)
{
- int rv = -EINVAL;
+ int rv = -ENOTTY;
struct ipmi_file_private *priv = file->private_data;
void __user *arg = (void __user *)data;
diff --git a/drivers/char/lp.c b/drivers/char/lp.c
index 97c3edb..2ff32c8 100644
--- a/drivers/char/lp.c
+++ b/drivers/char/lp.c
@@ -650,7 +650,7 @@ static int lp_do_ioctl(unsigned int minor, unsigned int cmd,
break;
default:
- retval = -EINVAL;
+ retval = -ENOTTY;
}
return retval;
}
diff --git a/drivers/char/nwflash.c b/drivers/char/nwflash.c
index a12f524..45b7a7a 100644
--- a/drivers/char/nwflash.c
+++ b/drivers/char/nwflash.c
@@ -115,7 +115,7 @@ static long flash_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
gbWriteBase64Enable = 0;
gbWriteEnable = 0;
mutex_unlock(&flash_mutex);
- return -EINVAL;
+ return -ENOTTY;
}
mutex_unlock(&flash_mutex);
return 0;
diff --git a/drivers/char/ppdev.c b/drivers/char/ppdev.c
index f176dba..8dce214 100644
--- a/drivers/char/ppdev.c
+++ b/drivers/char/ppdev.c
@@ -622,7 +622,7 @@ static int pp_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
default:
pr_debug(CHRDEV "%x: What? (cmd=0x%x)\n", minor, cmd);
- return -EINVAL;
+ return -ENOTTY;
}
/* Keep the compiler happy */
diff --git a/drivers/char/random.c b/drivers/char/random.c
index d4ddeba..40aad1c 100644
--- a/drivers/char/random.c
+++ b/drivers/char/random.c
@@ -1157,7 +1157,7 @@ static long random_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
rand_initialize();
return 0;
default:
- return -EINVAL;
+ return -ENOTTY;
}
}
diff --git a/drivers/char/raw.c b/drivers/char/raw.c
index b4b9d5a..a992bf1 100644
--- a/drivers/char/raw.c
+++ b/drivers/char/raw.c
@@ -231,7 +231,7 @@ static long raw_ctl_ioctl(struct file *filp, unsigned int command,
return 0;
}
- return -EINVAL;
+ return -ENOTTY;
}
#ifdef CONFIG_COMPAT
@@ -273,7 +273,7 @@ static long raw_ctl_compat_ioctl(struct file *file, unsigned int cmd,
return 0;
}
- return -EINVAL;
+ return -ENOTTY;
}
#endif
diff --git a/drivers/char/viotape.c b/drivers/char/viotape.c
index ad6e64a..a427d40 100644
--- a/drivers/char/viotape.c
+++ b/drivers/char/viotape.c
@@ -529,7 +529,7 @@ static int viotap_ioctl(struct inode *inode, struct file *file,
down(&reqSem);
- ret = -EINVAL;
+ ret = -ENOTTY;
switch (cmd) {
case MTIOCTOP:
diff --git a/fs/pipe.c b/fs/pipe.c
index da42f7d..fe7ffe4 100644
--- a/fs/pipe.c
+++ b/fs/pipe.c
@@ -665,7 +665,7 @@ static long pipe_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
return put_user(count, (int __user *)arg);
default:
- return -EINVAL;
+ return -ENOTTY;
}
}
diff --git a/net/core/dev.c b/net/core/dev.c
index c2ac599..b93c76d 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -4773,7 +4773,7 @@ static int dev_ifsioc_locked(struct net *net, struct ifreq *ifr, unsigned int cm
* is never reached
*/
WARN_ON(1);
- err = -EINVAL;
+ err = -ENOTTY;
break;
}
@@ -5041,7 +5041,7 @@ int dev_ioctl(struct net *net, unsigned int cmd, void __user *arg)
/* Set the per device memory buffer space.
* Not applicable in our case */
case SIOCSIFLINK:
- return -EINVAL;
+ return -EOPNOTSUPP;
/*
* Unknown or private ioctl.
@@ -5062,7 +5062,7 @@ int dev_ioctl(struct net *net, unsigned int cmd, void __user *arg)
/* Take care of Wireless Extensions */
if (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST)
return wext_handle_ioctl(net, &ifr, cmd, arg);
- return -EINVAL;
+ return -ENOTTY;
}
}
--
1.7.5.rc1
^ permalink raw reply related
* Re: [PATCH 0/6] sctp: Auto-ASCONF patch series
From: Michio Honda @ 2011-04-27 9:32 UTC (permalink / raw)
To: Wei Yongjun; +Cc: netdev, YOSHIFUJI Hideaki
In-Reply-To: <4DB7E1D1.8030104@cn.fujitsu.com>
mmm, can you send me the test program?
I'll fix ASAP!
- Michio
On Apr 27, 2011, at 18:28 , Wei Yongjun wrote:
>
>> From 9ede9db0ec4b03d3061a5bfed78328cb5528b908 Mon Sep 17 00:00:00 2001
>> From: Michio Honda <micchie@sfc.wide.ad.jp>
>> Date: Wed, 27 Apr 2011 17:16:21 +0900
>> Subject: [PATCH 0/6] sctp: Auto-ASCONF patch series
>>
>> Series of 6 patches to support auto_asconf and the other related functionalities that auto_asconf relies on.
>>
>> Michio Honda (5):
>> sctp: Add ADD/DEL ASCONF handling at the receiver.
>> sctp: Add Auto-ASCONF support (core).
>> sctp: Add sysctl support for Auto-ASCONF.
>> sctp: Add socket option operation for Auto-ASCONF.
>> sctp: sctp: Add ASCONF operation on the single-homed host
>>
>> YOSHIFUJI Hideaki (1):
>> sctp: Allow regular C expression in 4th argument for
>> SCTP_DEBUG_PRINTK_IPADDR macro.
>
>
> When I test this patchset using IPv4 only socket,
> Host1:
> #sctp_test -H 0.0.0.0 -P 9000 -l
> Host2:
> #sctp_test -H 0.0.0.0 -P 9000 -h 192.168.0.19 -p 9000 -s -x 1 -D -T
> I got following unexpected thing, IPv6 address be added to the asoc.
>
> Frame 3 (118 bytes on wire, 118 bytes captured)
> Arrival Time: Apr 27, 2011 17:17:59.572897000
> [Time delta from previous captured frame: 13.804056000 seconds]
> [Time delta from previous displayed frame: 13.804056000 seconds]
> [Time since reference or first frame: 13.804105000 seconds]
> Frame Number: 3
> Frame Length: 118 bytes
> Capture Length: 118 bytes
> [Frame is marked: False]
> [Protocols in frame: eth:ip:sctp]
> Ethernet II, Src: RealtekU_1a:f0:5c (52:54:00:1a:f0:5c), Dst: RealtekU_21:3c:7c (52:54:00:21:3c:7c)
> Destination: RealtekU_21:3c:7c (52:54:00:21:3c:7c)
> Address: RealtekU_21:3c:7c (52:54:00:21:3c:7c)
> .... ...0 .... .... .... .... = IG bit: Individual address (unicast)
> .... ..1. .... .... .... .... = LG bit: Locally administered address (this is NOT the factory default)
> Source: RealtekU_1a:f0:5c (52:54:00:1a:f0:5c)
> Address: RealtekU_1a:f0:5c (52:54:00:1a:f0:5c)
> .... ...0 .... .... .... .... = IG bit: Individual address (unicast)
> .... ..1. .... .... .... .... = LG bit: Locally administered address (this is NOT the factory default)
> Type: IP (0x0800)
> Internet Protocol, Src: 192.168.0.21 (192.168.0.21), Dst: 192.168.0.19 (192.168.0.19)
> Version: 4
> Header length: 20 bytes
> Differentiated Services Field: 0x02 (DSCP 0x00: Default; ECN: 0x02)
> 0000 00.. = Differentiated Services Codepoint: Default (0x00)
> .... ..1. = ECN-Capable Transport (ECT): 1
> .... ...0 = ECN-CE: 0
> Total Length: 104
> Identification: 0x001e (30)
> Flags: 0x02 (Don't Fragment)
> 0.. = Reserved bit: Not Set
> .1. = Don't fragment: Set
> ..0 = More fragments: Not Set
> Fragment offset: 0
> Time to live: 64
> Protocol: SCTP (0x84)
> Header checksum: 0xb879 [correct]
> [Good: True]
> [Bad : False]
> Source: 192.168.0.21 (192.168.0.21)
> Destination: 192.168.0.19 (192.168.0.19)
> Stream Control Transmission Protocol, Src Port: 9000 (9000), Dst Port: 9000 (9000)
> Source port: 9000
> Destination port: 9000
> Verification tag: 0x2b1e0377
> Checksum: 0x2a2e5aff (not verified)
> AUTH chunk
> Chunk type: AUTH (15)
> 0... .... = Bit: Stop processing of the packet
> .0.. .... = Bit: Do not report
> Chunk flags: 0x00
> Chunk length: 28
> Shared key identifier: 0
> HMAC identifier: SHA-1 (1)
> HMAC: AA21B3835E1069FBFFA3589B1EB4C0AE47021F15
> ASCONF chunk
> Chunk type: ASCONF (193)
> 1... .... = Bit: Skip chunk and continue processing of the packet
> .1.. .... = Bit: Do report
> Chunk flags: 0x00
> Chunk length: 44
> Serial number: 0xfa0b220b
> IPv4 address parameter (Address: 192.168.1.21)
> Parameter type: IPv4 address (0x0005)
> 0... .... .... .... = Bit: Stop processing of chunk
> .0.. .... .... .... = Bit: Do not report
> Parameter length: 8
> IP Version 4 address: 192.168.1.21 (192.168.1.21)
> Delete IP address parameter (Address: 3ffe:501:ffff:101:5054:ff:fe70:993d, correlation ID: 0)
> Parameter type: Delete IP address (0xc002)
> 1... .... .... .... = Bit: Skip parameter and continue processing of the chunk
> .1.. .... .... .... = Bit: Do report
> Parameter length: 28
> Correlation_id: 0x00000000
> IPv6 address parameter (Address: 3ffe:501:ffff:101:5054:ff:fe70:993d)
> Parameter type: IPv6 address (0x0006)
> 0... .... .... .... = Bit: Stop processing of chunk
> .0.. .... .... .... = Bit: Do not report
> Parameter length: 20
> IP Version 6 address: 3ffe:501:ffff:101:5054:ff:fe70:993d (3ffe:501:ffff:101:5054:ff:fe70:993d)
>
> No. Time Source Destination Protocol Info
> 4 13.804193 192.168.0.19 192.168.0.21 SCTP AUTH ASCONF_ACK
>
> Frame 4 (122 bytes on wire, 122 bytes captured)
> Arrival Time: Apr 27, 2011 17:17:59.572985000
> [Time delta from previous captured frame: 0.000088000 seconds]
> [Time delta from previous displayed frame: 0.000088000 seconds]
> [Time since reference or first frame: 13.804193000 seconds]
> Frame Number: 4
> Frame Length: 122 bytes
> Capture Length: 122 bytes
> [Frame is marked: False]
> [Protocols in frame: eth:ip:sctp]
> Ethernet II, Src: RealtekU_21:3c:7c (52:54:00:21:3c:7c), Dst: RealtekU_1a:f0:5c (52:54:00:1a:f0:5c)
> Destination: RealtekU_1a:f0:5c (52:54:00:1a:f0:5c)
> Address: RealtekU_1a:f0:5c (52:54:00:1a:f0:5c)
> .... ...0 .... .... .... .... = IG bit: Individual address (unicast)
> .... ..1. .... .... .... .... = LG bit: Locally administered address (this is NOT the factory default)
> Source: RealtekU_21:3c:7c (52:54:00:21:3c:7c)
> Address: RealtekU_21:3c:7c (52:54:00:21:3c:7c)
> .... ...0 .... .... .... .... = IG bit: Individual address (unicast)
> .... ..1. .... .... .... .... = LG bit: Locally administered address (this is NOT the factory default)
> Type: IP (0x0800)
> Internet Protocol, Src: 192.168.0.19 (192.168.0.19), Dst: 192.168.0.21 (192.168.0.21)
> Version: 4
> Header length: 20 bytes
> Differentiated Services Field: 0x02 (DSCP 0x00: Default; ECN: 0x02)
> 0000 00.. = Differentiated Services Codepoint: Default (0x00)
> .... ..1. = ECN-Capable Transport (ECT): 1
> .... ...0 = ECN-CE: 0
> Total Length: 108
> Identification: 0x0000 (0)
> Flags: 0x02 (Don't Fragment)
> 0.. = Reserved bit: Not Set
> .1. = Don't fragment: Set
> ..0 = More fragments: Not Set
> Fragment offset: 0
> Time to live: 64
> Protocol: SCTP (0x84)
> Header checksum: 0xb893 [correct]
> [Good: True]
> [Bad : False]
> Source: 192.168.0.19 (192.168.0.19)
> Destination: 192.168.0.21 (192.168.0.21)
> Stream Control Transmission Protocol, Src Port: 9000 (9000), Dst Port: 9000 (9000)
> Source port: 9000
> Destination port: 9000
> Verification tag: 0x1b2d2288
> Checksum: 0x9b288dab (not verified)
> AUTH chunk
> Chunk type: AUTH (15)
> 0... .... = Bit: Stop processing of the packet
> .0.. .... = Bit: Do not report
> Chunk flags: 0x00
> Chunk length: 28
> Shared key identifier: 0
> HMAC identifier: SHA-1 (1)
> HMAC: DDB8D3766671957FABA3554DE588599C9624DCBB
> ASCONF_ACK chunk
> Chunk type: ASCONF_ACK (128)
> 1... .... = Bit: Skip chunk and continue processing of the packet
> .0.. .... = Bit: Do not report
> Chunk flags: 0x00
> Chunk length: 48
> Serial number: 0xfa0b220b
> Error cause indication parameter
> Parameter type: Error cause indication (0xc003)
> 1... .... .... .... = Bit: Skip parameter and continue processing of the chunk
> .1.. .... .... .... = Bit: Do report
> Parameter length: 40
> Correlation_id: 0x00000000
> Unresolvable address cause (Address: )
> Cause code: Unresolvable address (0x0005)
> Cause length: 32
> Delete IP address parameter (Address: 3ffe:501:ffff:101:5054:ff:fe70:993d, correlation ID: 0)
> Parameter type: Delete IP address (0xc002)
> 1... .... .... .... = Bit: Skip parameter and continue processing of the chunk
> .1.. .... .... .... = Bit: Do report
> Parameter length: 28
> Correlation_id: 0x00000000
> IPv6 address parameter (Address: 3ffe:501:ffff:101:5054:ff:fe70:993d)
> Parameter type: IPv6 address (0x0006)
> 0... .... .... .... = Bit: Stop processing of chunk
> .0.. .... .... .... = Bit: Do not report
> Parameter length: 20
> IP Version 6 address: 3ffe:501:ffff:101:5054:ff:fe70:993d (3ffe:501:ffff:101:5054:ff:fe70:993d)
> Frame 5 (118 bytes on wire, 118 bytes captured)
> Arrival Time: Apr 27, 2011 17:17:59.573941000
> [Time delta from previous captured frame: 0.000956000 seconds]
> [Time delta from previous displayed frame: 0.000956000 seconds]
> [Time since reference or first frame: 13.805149000 seconds]
> Frame Number: 5
> Frame Length: 118 bytes
> Capture Length: 118 bytes
> [Frame is marked: False]
> [Protocols in frame: eth:ip:sctp]
> Ethernet II, Src: RealtekU_1a:f0:5c (52:54:00:1a:f0:5c), Dst: RealtekU_21:3c:7c (52:54:00:21:3c:7c)
> Destination: RealtekU_21:3c:7c (52:54:00:21:3c:7c)
> Address: RealtekU_21:3c:7c (52:54:00:21:3c:7c)
> .... ...0 .... .... .... .... = IG bit: Individual address (unicast)
> .... ..1. .... .... .... .... = LG bit: Locally administered address (this is NOT the factory default)
> Source: RealtekU_1a:f0:5c (52:54:00:1a:f0:5c)
> Address: RealtekU_1a:f0:5c (52:54:00:1a:f0:5c)
> .... ...0 .... .... .... .... = IG bit: Individual address (unicast)
> .... ..1. .... .... .... .... = LG bit: Locally administered address (this is NOT the factory default)
> Type: IP (0x0800)
> Internet Protocol, Src: 192.168.0.21 (192.168.0.21), Dst: 192.168.0.19 (192.168.0.19)
> Version: 4
> Header length: 20 bytes
> Differentiated Services Field: 0x02 (DSCP 0x00: Default; ECN: 0x02)
> 0000 00.. = Differentiated Services Codepoint: Default (0x00)
> .... ..1. = ECN-Capable Transport (ECT): 1
> .... ...0 = ECN-CE: 0
> Total Length: 104
> Identification: 0x001f (31)
> Flags: 0x02 (Don't Fragment)
> 0.. = Reserved bit: Not Set
> .1. = Don't fragment: Set
> ..0 = More fragments: Not Set
> Fragment offset: 0
> Time to live: 64
> Protocol: SCTP (0x84)
> Header checksum: 0xb878 [correct]
> [Good: True]
> [Bad : False]
> Source: 192.168.0.21 (192.168.0.21)
> Destination: 192.168.0.19 (192.168.0.19)
> Stream Control Transmission Protocol, Src Port: 9000 (9000), Dst Port: 9000 (9000)
> Source port: 9000
> Destination port: 9000
> Verification tag: 0x2b1e0377
> Checksum: 0x91cfddc0 (not verified)
> AUTH chunk
> Chunk type: AUTH (15)
> 0... .... = Bit: Stop processing of the packet
> .0.. .... = Bit: Do not report
> Chunk flags: 0x00
> Chunk length: 28
> Shared key identifier: 0
> HMAC identifier: SHA-1 (1)
> HMAC: 2293892E2642A2D0E8911A669761E86134A47BD0
> ASCONF chunk
> Chunk type: ASCONF (193)
> 1... .... = Bit: Skip chunk and continue processing of the packet
> .1.. .... = Bit: Do report
> Chunk flags: 0x00
> Chunk length: 44
> Serial number: 0xfa0b220c
> IPv6 address parameter (Address: 3ffe:501:ffff:101:5054:ff:fe70:993d)
> Parameter type: IPv6 address (0x0006)
> 0... .... .... .... = Bit: Stop processing of chunk
> .0.. .... .... .... = Bit: Do not report
> Parameter length: 20
> IP Version 6 address: 3ffe:501:ffff:101:5054:ff:fe70:993d (3ffe:501:ffff:101:5054:ff:fe70:993d)
> Delete IP address parameter (Address: 192.168.1.21, correlation ID: 0)
> Parameter type: Delete IP address (0xc002)
> 1... .... .... .... = Bit: Skip parameter and continue processing of the chunk
> .1.. .... .... .... = Bit: Do report
> Parameter length: 16
> Correlation_id: 0x00000000
> IPv4 address parameter (Address: 192.168.1.21)
> Parameter type: IPv4 address (0x0005)
> 0... .... .... .... = Bit: Stop processing of chunk
> .0.. .... .... .... = Bit: Do not report
> Parameter length: 8
> IP Version 4 address: 192.168.1.21 (192.168.1.21)
>
> No. Time Source Destination Protocol Info
> 6 13.805190 192.168.0.19 192.168.0.21 SCTP AUTH ASCONF_ACK
>
> Frame 6 (82 bytes on wire, 82 bytes captured)
> Arrival Time: Apr 27, 2011 17:17:59.573982000
> [Time delta from previous captured frame: 0.000041000 seconds]
> [Time delta from previous displayed frame: 0.000041000 seconds]
> [Time since reference or first frame: 13.805190000 seconds]
> Frame Number: 6
> Frame Length: 82 bytes
> Capture Length: 82 bytes
> [Frame is marked: False]
> [Protocols in frame: eth:ip:sctp]
> Ethernet II, Src: RealtekU_21:3c:7c (52:54:00:21:3c:7c), Dst: RealtekU_1a:f0:5c (52:54:00:1a:f0:5c)
> Destination: RealtekU_1a:f0:5c (52:54:00:1a:f0:5c)
> Address: RealtekU_1a:f0:5c (52:54:00:1a:f0:5c)
> .... ...0 .... .... .... .... = IG bit: Individual address (unicast)
> .... ..1. .... .... .... .... = LG bit: Locally administered address (this is NOT the factory default)
> Source: RealtekU_21:3c:7c (52:54:00:21:3c:7c)
> Address: RealtekU_21:3c:7c (52:54:00:21:3c:7c)
> .... ...0 .... .... .... .... = IG bit: Individual address (unicast)
> .... ..1. .... .... .... .... = LG bit: Locally administered address (this is NOT the factory default)
> Type: IP (0x0800)
> Internet Protocol, Src: 192.168.0.19 (192.168.0.19), Dst: 192.168.0.21 (192.168.0.21)
> Version: 4
> Header length: 20 bytes
> Differentiated Services Field: 0x02 (DSCP 0x00: Default; ECN: 0x02)
> 0000 00.. = Differentiated Services Codepoint: Default (0x00)
> .... ..1. = ECN-Capable Transport (ECT): 1
> .... ...0 = ECN-CE: 0
> Total Length: 68
> Identification: 0x0000 (0)
> Flags: 0x02 (Don't Fragment)
> 0.. = Reserved bit: Not Set
> .1. = Don't fragment: Set
> ..0 = More fragments: Not Set
> Fragment offset: 0
> Time to live: 64
> Protocol: SCTP (0x84)
> Header checksum: 0xb8bb [correct]
> [Good: True]
> [Bad : False]
> Source: 192.168.0.19 (192.168.0.19)
> Destination: 192.168.0.21 (192.168.0.21)
> Stream Control Transmission Protocol, Src Port: 9000 (9000), Dst Port: 9000 (9000)
> Source port: 9000
> Destination port: 9000
> Verification tag: 0x1b2d2288
> Checksum: 0x0962e46c (not verified)
> AUTH chunk
> Chunk type: AUTH (15)
> 0... .... = Bit: Stop processing of the packet
> .0.. .... = Bit: Do not report
> Chunk flags: 0x00
> Chunk length: 28
> Shared key identifier: 0
> HMAC identifier: SHA-1 (1)
> HMAC: DBE65D3AB3FB77D29FE2870BBB9ECCE4DC6D5EB6
> ASCONF_ACK chunk
> Chunk type: ASCONF_ACK (128)
> 1... .... = Bit: Skip chunk and continue processing of the packet
> .0.. .... = Bit: Do not report
> Chunk flags: 0x00
> Chunk length: 8
> Serial number: 0xfa0b220c
>
> --
> To unsubscribe from this list: send the line "unsubscribe netdev" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH 0/6] sctp: Auto-ASCONF patch series
From: Wei Yongjun @ 2011-04-27 9:28 UTC (permalink / raw)
To: Michio Honda; +Cc: netdev, YOSHIFUJI Hideaki
In-Reply-To: <AC642DCE-402B-496B-B519-679335A13211@sfc.wide.ad.jp>
> From 9ede9db0ec4b03d3061a5bfed78328cb5528b908 Mon Sep 17 00:00:00 2001
> From: Michio Honda <micchie@sfc.wide.ad.jp>
> Date: Wed, 27 Apr 2011 17:16:21 +0900
> Subject: [PATCH 0/6] sctp: Auto-ASCONF patch series
>
> Series of 6 patches to support auto_asconf and the other related functionalities that auto_asconf relies on.
>
> Michio Honda (5):
> sctp: Add ADD/DEL ASCONF handling at the receiver.
> sctp: Add Auto-ASCONF support (core).
> sctp: Add sysctl support for Auto-ASCONF.
> sctp: Add socket option operation for Auto-ASCONF.
> sctp: sctp: Add ASCONF operation on the single-homed host
>
> YOSHIFUJI Hideaki (1):
> sctp: Allow regular C expression in 4th argument for
> SCTP_DEBUG_PRINTK_IPADDR macro.
When I test this patchset using IPv4 only socket,
Host1:
#sctp_test -H 0.0.0.0 -P 9000 -l
Host2:
#sctp_test -H 0.0.0.0 -P 9000 -h 192.168.0.19 -p 9000 -s -x 1 -D -T
I got following unexpected thing, IPv6 address be added to the asoc.
Frame 3 (118 bytes on wire, 118 bytes captured)
Arrival Time: Apr 27, 2011 17:17:59.572897000
[Time delta from previous captured frame: 13.804056000 seconds]
[Time delta from previous displayed frame: 13.804056000 seconds]
[Time since reference or first frame: 13.804105000 seconds]
Frame Number: 3
Frame Length: 118 bytes
Capture Length: 118 bytes
[Frame is marked: False]
[Protocols in frame: eth:ip:sctp]
Ethernet II, Src: RealtekU_1a:f0:5c (52:54:00:1a:f0:5c), Dst: RealtekU_21:3c:7c (52:54:00:21:3c:7c)
Destination: RealtekU_21:3c:7c (52:54:00:21:3c:7c)
Address: RealtekU_21:3c:7c (52:54:00:21:3c:7c)
.... ...0 .... .... .... .... = IG bit: Individual address (unicast)
.... ..1. .... .... .... .... = LG bit: Locally administered address (this is NOT the factory default)
Source: RealtekU_1a:f0:5c (52:54:00:1a:f0:5c)
Address: RealtekU_1a:f0:5c (52:54:00:1a:f0:5c)
.... ...0 .... .... .... .... = IG bit: Individual address (unicast)
.... ..1. .... .... .... .... = LG bit: Locally administered address (this is NOT the factory default)
Type: IP (0x0800)
Internet Protocol, Src: 192.168.0.21 (192.168.0.21), Dst: 192.168.0.19 (192.168.0.19)
Version: 4
Header length: 20 bytes
Differentiated Services Field: 0x02 (DSCP 0x00: Default; ECN: 0x02)
0000 00.. = Differentiated Services Codepoint: Default (0x00)
.... ..1. = ECN-Capable Transport (ECT): 1
.... ...0 = ECN-CE: 0
Total Length: 104
Identification: 0x001e (30)
Flags: 0x02 (Don't Fragment)
0.. = Reserved bit: Not Set
.1. = Don't fragment: Set
..0 = More fragments: Not Set
Fragment offset: 0
Time to live: 64
Protocol: SCTP (0x84)
Header checksum: 0xb879 [correct]
[Good: True]
[Bad : False]
Source: 192.168.0.21 (192.168.0.21)
Destination: 192.168.0.19 (192.168.0.19)
Stream Control Transmission Protocol, Src Port: 9000 (9000), Dst Port: 9000 (9000)
Source port: 9000
Destination port: 9000
Verification tag: 0x2b1e0377
Checksum: 0x2a2e5aff (not verified)
AUTH chunk
Chunk type: AUTH (15)
0... .... = Bit: Stop processing of the packet
.0.. .... = Bit: Do not report
Chunk flags: 0x00
Chunk length: 28
Shared key identifier: 0
HMAC identifier: SHA-1 (1)
HMAC: AA21B3835E1069FBFFA3589B1EB4C0AE47021F15
ASCONF chunk
Chunk type: ASCONF (193)
1... .... = Bit: Skip chunk and continue processing of the packet
.1.. .... = Bit: Do report
Chunk flags: 0x00
Chunk length: 44
Serial number: 0xfa0b220b
IPv4 address parameter (Address: 192.168.1.21)
Parameter type: IPv4 address (0x0005)
0... .... .... .... = Bit: Stop processing of chunk
.0.. .... .... .... = Bit: Do not report
Parameter length: 8
IP Version 4 address: 192.168.1.21 (192.168.1.21)
Delete IP address parameter (Address: 3ffe:501:ffff:101:5054:ff:fe70:993d, correlation ID: 0)
Parameter type: Delete IP address (0xc002)
1... .... .... .... = Bit: Skip parameter and continue processing of the chunk
.1.. .... .... .... = Bit: Do report
Parameter length: 28
Correlation_id: 0x00000000
IPv6 address parameter (Address: 3ffe:501:ffff:101:5054:ff:fe70:993d)
Parameter type: IPv6 address (0x0006)
0... .... .... .... = Bit: Stop processing of chunk
.0.. .... .... .... = Bit: Do not report
Parameter length: 20
IP Version 6 address: 3ffe:501:ffff:101:5054:ff:fe70:993d (3ffe:501:ffff:101:5054:ff:fe70:993d)
No. Time Source Destination Protocol Info
4 13.804193 192.168.0.19 192.168.0.21 SCTP AUTH ASCONF_ACK
Frame 4 (122 bytes on wire, 122 bytes captured)
Arrival Time: Apr 27, 2011 17:17:59.572985000
[Time delta from previous captured frame: 0.000088000 seconds]
[Time delta from previous displayed frame: 0.000088000 seconds]
[Time since reference or first frame: 13.804193000 seconds]
Frame Number: 4
Frame Length: 122 bytes
Capture Length: 122 bytes
[Frame is marked: False]
[Protocols in frame: eth:ip:sctp]
Ethernet II, Src: RealtekU_21:3c:7c (52:54:00:21:3c:7c), Dst: RealtekU_1a:f0:5c (52:54:00:1a:f0:5c)
Destination: RealtekU_1a:f0:5c (52:54:00:1a:f0:5c)
Address: RealtekU_1a:f0:5c (52:54:00:1a:f0:5c)
.... ...0 .... .... .... .... = IG bit: Individual address (unicast)
.... ..1. .... .... .... .... = LG bit: Locally administered address (this is NOT the factory default)
Source: RealtekU_21:3c:7c (52:54:00:21:3c:7c)
Address: RealtekU_21:3c:7c (52:54:00:21:3c:7c)
.... ...0 .... .... .... .... = IG bit: Individual address (unicast)
.... ..1. .... .... .... .... = LG bit: Locally administered address (this is NOT the factory default)
Type: IP (0x0800)
Internet Protocol, Src: 192.168.0.19 (192.168.0.19), Dst: 192.168.0.21 (192.168.0.21)
Version: 4
Header length: 20 bytes
Differentiated Services Field: 0x02 (DSCP 0x00: Default; ECN: 0x02)
0000 00.. = Differentiated Services Codepoint: Default (0x00)
.... ..1. = ECN-Capable Transport (ECT): 1
.... ...0 = ECN-CE: 0
Total Length: 108
Identification: 0x0000 (0)
Flags: 0x02 (Don't Fragment)
0.. = Reserved bit: Not Set
.1. = Don't fragment: Set
..0 = More fragments: Not Set
Fragment offset: 0
Time to live: 64
Protocol: SCTP (0x84)
Header checksum: 0xb893 [correct]
[Good: True]
[Bad : False]
Source: 192.168.0.19 (192.168.0.19)
Destination: 192.168.0.21 (192.168.0.21)
Stream Control Transmission Protocol, Src Port: 9000 (9000), Dst Port: 9000 (9000)
Source port: 9000
Destination port: 9000
Verification tag: 0x1b2d2288
Checksum: 0x9b288dab (not verified)
AUTH chunk
Chunk type: AUTH (15)
0... .... = Bit: Stop processing of the packet
.0.. .... = Bit: Do not report
Chunk flags: 0x00
Chunk length: 28
Shared key identifier: 0
HMAC identifier: SHA-1 (1)
HMAC: DDB8D3766671957FABA3554DE588599C9624DCBB
ASCONF_ACK chunk
Chunk type: ASCONF_ACK (128)
1... .... = Bit: Skip chunk and continue processing of the packet
.0.. .... = Bit: Do not report
Chunk flags: 0x00
Chunk length: 48
Serial number: 0xfa0b220b
Error cause indication parameter
Parameter type: Error cause indication (0xc003)
1... .... .... .... = Bit: Skip parameter and continue processing of the chunk
.1.. .... .... .... = Bit: Do report
Parameter length: 40
Correlation_id: 0x00000000
Unresolvable address cause (Address: )
Cause code: Unresolvable address (0x0005)
Cause length: 32
Delete IP address parameter (Address: 3ffe:501:ffff:101:5054:ff:fe70:993d, correlation ID: 0)
Parameter type: Delete IP address (0xc002)
1... .... .... .... = Bit: Skip parameter and continue processing of the chunk
.1.. .... .... .... = Bit: Do report
Parameter length: 28
Correlation_id: 0x00000000
IPv6 address parameter (Address: 3ffe:501:ffff:101:5054:ff:fe70:993d)
Parameter type: IPv6 address (0x0006)
0... .... .... .... = Bit: Stop processing of chunk
.0.. .... .... .... = Bit: Do not report
Parameter length: 20
IP Version 6 address: 3ffe:501:ffff:101:5054:ff:fe70:993d (3ffe:501:ffff:101:5054:ff:fe70:993d)
Frame 5 (118 bytes on wire, 118 bytes captured)
Arrival Time: Apr 27, 2011 17:17:59.573941000
[Time delta from previous captured frame: 0.000956000 seconds]
[Time delta from previous displayed frame: 0.000956000 seconds]
[Time since reference or first frame: 13.805149000 seconds]
Frame Number: 5
Frame Length: 118 bytes
Capture Length: 118 bytes
[Frame is marked: False]
[Protocols in frame: eth:ip:sctp]
Ethernet II, Src: RealtekU_1a:f0:5c (52:54:00:1a:f0:5c), Dst: RealtekU_21:3c:7c (52:54:00:21:3c:7c)
Destination: RealtekU_21:3c:7c (52:54:00:21:3c:7c)
Address: RealtekU_21:3c:7c (52:54:00:21:3c:7c)
.... ...0 .... .... .... .... = IG bit: Individual address (unicast)
.... ..1. .... .... .... .... = LG bit: Locally administered address (this is NOT the factory default)
Source: RealtekU_1a:f0:5c (52:54:00:1a:f0:5c)
Address: RealtekU_1a:f0:5c (52:54:00:1a:f0:5c)
.... ...0 .... .... .... .... = IG bit: Individual address (unicast)
.... ..1. .... .... .... .... = LG bit: Locally administered address (this is NOT the factory default)
Type: IP (0x0800)
Internet Protocol, Src: 192.168.0.21 (192.168.0.21), Dst: 192.168.0.19 (192.168.0.19)
Version: 4
Header length: 20 bytes
Differentiated Services Field: 0x02 (DSCP 0x00: Default; ECN: 0x02)
0000 00.. = Differentiated Services Codepoint: Default (0x00)
.... ..1. = ECN-Capable Transport (ECT): 1
.... ...0 = ECN-CE: 0
Total Length: 104
Identification: 0x001f (31)
Flags: 0x02 (Don't Fragment)
0.. = Reserved bit: Not Set
.1. = Don't fragment: Set
..0 = More fragments: Not Set
Fragment offset: 0
Time to live: 64
Protocol: SCTP (0x84)
Header checksum: 0xb878 [correct]
[Good: True]
[Bad : False]
Source: 192.168.0.21 (192.168.0.21)
Destination: 192.168.0.19 (192.168.0.19)
Stream Control Transmission Protocol, Src Port: 9000 (9000), Dst Port: 9000 (9000)
Source port: 9000
Destination port: 9000
Verification tag: 0x2b1e0377
Checksum: 0x91cfddc0 (not verified)
AUTH chunk
Chunk type: AUTH (15)
0... .... = Bit: Stop processing of the packet
.0.. .... = Bit: Do not report
Chunk flags: 0x00
Chunk length: 28
Shared key identifier: 0
HMAC identifier: SHA-1 (1)
HMAC: 2293892E2642A2D0E8911A669761E86134A47BD0
ASCONF chunk
Chunk type: ASCONF (193)
1... .... = Bit: Skip chunk and continue processing of the packet
.1.. .... = Bit: Do report
Chunk flags: 0x00
Chunk length: 44
Serial number: 0xfa0b220c
IPv6 address parameter (Address: 3ffe:501:ffff:101:5054:ff:fe70:993d)
Parameter type: IPv6 address (0x0006)
0... .... .... .... = Bit: Stop processing of chunk
.0.. .... .... .... = Bit: Do not report
Parameter length: 20
IP Version 6 address: 3ffe:501:ffff:101:5054:ff:fe70:993d (3ffe:501:ffff:101:5054:ff:fe70:993d)
Delete IP address parameter (Address: 192.168.1.21, correlation ID: 0)
Parameter type: Delete IP address (0xc002)
1... .... .... .... = Bit: Skip parameter and continue processing of the chunk
.1.. .... .... .... = Bit: Do report
Parameter length: 16
Correlation_id: 0x00000000
IPv4 address parameter (Address: 192.168.1.21)
Parameter type: IPv4 address (0x0005)
0... .... .... .... = Bit: Stop processing of chunk
.0.. .... .... .... = Bit: Do not report
Parameter length: 8
IP Version 4 address: 192.168.1.21 (192.168.1.21)
No. Time Source Destination Protocol Info
6 13.805190 192.168.0.19 192.168.0.21 SCTP AUTH ASCONF_ACK
Frame 6 (82 bytes on wire, 82 bytes captured)
Arrival Time: Apr 27, 2011 17:17:59.573982000
[Time delta from previous captured frame: 0.000041000 seconds]
[Time delta from previous displayed frame: 0.000041000 seconds]
[Time since reference or first frame: 13.805190000 seconds]
Frame Number: 6
Frame Length: 82 bytes
Capture Length: 82 bytes
[Frame is marked: False]
[Protocols in frame: eth:ip:sctp]
Ethernet II, Src: RealtekU_21:3c:7c (52:54:00:21:3c:7c), Dst: RealtekU_1a:f0:5c (52:54:00:1a:f0:5c)
Destination: RealtekU_1a:f0:5c (52:54:00:1a:f0:5c)
Address: RealtekU_1a:f0:5c (52:54:00:1a:f0:5c)
.... ...0 .... .... .... .... = IG bit: Individual address (unicast)
.... ..1. .... .... .... .... = LG bit: Locally administered address (this is NOT the factory default)
Source: RealtekU_21:3c:7c (52:54:00:21:3c:7c)
Address: RealtekU_21:3c:7c (52:54:00:21:3c:7c)
.... ...0 .... .... .... .... = IG bit: Individual address (unicast)
.... ..1. .... .... .... .... = LG bit: Locally administered address (this is NOT the factory default)
Type: IP (0x0800)
Internet Protocol, Src: 192.168.0.19 (192.168.0.19), Dst: 192.168.0.21 (192.168.0.21)
Version: 4
Header length: 20 bytes
Differentiated Services Field: 0x02 (DSCP 0x00: Default; ECN: 0x02)
0000 00.. = Differentiated Services Codepoint: Default (0x00)
.... ..1. = ECN-Capable Transport (ECT): 1
.... ...0 = ECN-CE: 0
Total Length: 68
Identification: 0x0000 (0)
Flags: 0x02 (Don't Fragment)
0.. = Reserved bit: Not Set
.1. = Don't fragment: Set
..0 = More fragments: Not Set
Fragment offset: 0
Time to live: 64
Protocol: SCTP (0x84)
Header checksum: 0xb8bb [correct]
[Good: True]
[Bad : False]
Source: 192.168.0.19 (192.168.0.19)
Destination: 192.168.0.21 (192.168.0.21)
Stream Control Transmission Protocol, Src Port: 9000 (9000), Dst Port: 9000 (9000)
Source port: 9000
Destination port: 9000
Verification tag: 0x1b2d2288
Checksum: 0x0962e46c (not verified)
AUTH chunk
Chunk type: AUTH (15)
0... .... = Bit: Stop processing of the packet
.0.. .... = Bit: Do not report
Chunk flags: 0x00
Chunk length: 28
Shared key identifier: 0
HMAC identifier: SHA-1 (1)
HMAC: DBE65D3AB3FB77D29FE2870BBB9ECCE4DC6D5EB6
ASCONF_ACK chunk
Chunk type: ASCONF_ACK (128)
1... .... = Bit: Skip chunk and continue processing of the packet
.0.. .... = Bit: Do not report
Chunk flags: 0x00
Chunk length: 8
Serial number: 0xfa0b220c
^ permalink raw reply
* [PATCH v4 5/5] iproute2: add can-j1939 support
From: Kurt Van Dijck @ 2011-04-27 9:03 UTC (permalink / raw)
To: socketcan-core-0fE9KPoRgkgATYTw5x5z8w,
netdev-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20110427085330.GA757-ozGf4kBk5synFtIcQ8t7k3L8HoS0Hn3T@public.gmane.org>
Add j1939 support to iproute2
Signed-off-by: Kurt Van Dijck <kurt.van.dijck-/BeEPy95v10@public.gmane.org>
---
diff --git a/Makefile b/Makefile
index d1ace1f..06bf281 100644
--- a/Makefile
+++ b/Makefile
@@ -27,9 +27,12 @@ ADDLIB+=dnet_ntop.o dnet_pton.o
#options for ipx
ADDLIB+=ipx_ntop.o ipx_pton.o
+#options for j1939
+ADDLIB+=j1939.o
+
CC = gcc
HOSTCC = gcc
-CCOPTS = -D_GNU_SOURCE -O2 -Wstrict-prototypes -Wall
+CCOPTS = -D_GNU_SOURCE -Wstrict-prototypes -Wall
CFLAGS = $(CCOPTS) -I../include $(DEFINES)
YACCFLAGS = -d -t -v
diff --git a/include/linux/can.h b/include/linux/can.h
new file mode 100644
index 0000000..9c2523c
--- /dev/null
+++ b/include/linux/can.h
@@ -0,0 +1,129 @@
+/*
+ * linux/can.h
+ *
+ * Definitions for CAN network layer (socket addr / CAN frame / CAN filter)
+ *
+ * Authors: Oliver Hartkopp <oliver.hartkopp-l29pVbxQd1IUtdQbppsyvg@public.gmane.org>
+ * Urs Thuermann <urs.thuermann-l29pVbxQd1IUtdQbppsyvg@public.gmane.org>
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Send feedback to <socketcan-users-0fE9KPoRgkgATYTw5x5z8w@public.gmane.org>
+ *
+ */
+
+#ifndef CAN_H
+#define CAN_H
+
+#include <linux/types.h>
+#include <linux/socket.h>
+
+/* controller area network (CAN) kernel definitions */
+
+/* special address description flags for the CAN_ID */
+#define CAN_EFF_FLAG 0x80000000U /* EFF/SFF is set in the MSB */
+#define CAN_RTR_FLAG 0x40000000U /* remote transmission request */
+#define CAN_ERR_FLAG 0x20000000U /* error frame */
+
+/* valid bits in CAN ID for frame formats */
+#define CAN_SFF_MASK 0x000007FFU /* standard frame format (SFF) */
+#define CAN_EFF_MASK 0x1FFFFFFFU /* extended frame format (EFF) */
+#define CAN_ERR_MASK 0x1FFFFFFFU /* omit EFF, RTR, ERR flags */
+
+/*
+ * Controller Area Network Identifier structure
+ *
+ * bit 0-28 : CAN identifier (11/29 bit)
+ * bit 29 : error frame flag (0 = data frame, 1 = error frame)
+ * bit 30 : remote transmission request flag (1 = rtr frame)
+ * bit 31 : frame format flag (0 = standard 11 bit, 1 = extended 29 bit)
+ */
+typedef __u32 canid_t;
+
+/*
+ * Controller Area Network Error Frame Mask structure
+ *
+ * bit 0-28 : error class mask (see include/linux/can/error.h)
+ * bit 29-31 : set to zero
+ */
+typedef __u32 can_err_mask_t;
+
+/**
+ * struct can_frame - basic CAN frame structure
+ * @can_id: the CAN ID of the frame and CAN_*_FLAG flags, see above.
+ * @can_dlc: the data length field of the CAN frame
+ * @data: the CAN frame payload.
+ */
+struct can_frame {
+ canid_t can_id; /* 32 bit CAN_ID + EFF/RTR/ERR flags */
+ __u8 can_dlc; /* data length code: 0 .. 8 */
+ __u8 data[8] __attribute__((aligned(8)));
+};
+
+/* particular protocols of the protocol family PF_CAN */
+#define CAN_RAW 1 /* RAW sockets */
+#define CAN_BCM 2 /* Broadcast Manager */
+#define CAN_TP16 3 /* VAG Transport Protocol v1.6 */
+#define CAN_TP20 4 /* VAG Transport Protocol v2.0 */
+#define CAN_MCNET 5 /* Bosch MCNet */
+#define CAN_ISOTP 6 /* ISO 15765-2 Transport Protocol */
+#define CAN_J1939 7 /* SAE J1939 */
+#define CAN_NPROTO 8
+
+#define SOL_CAN_BASE 100
+
+/**
+ * struct sockaddr_can - the sockaddr structure for CAN sockets
+ * @can_family: address family number AF_CAN.
+ * @can_ifindex: CAN network interface index.
+ * @can_addr: protocol specific address information
+ */
+struct sockaddr_can {
+ sa_family_t can_family;
+ int can_ifindex;
+ union {
+ /* transport protocol class address information (e.g. ISOTP) */
+ struct { canid_t rx_id, tx_id; } tp;
+
+ /* J1939 address information */
+ struct {
+ /* 8 byte name when using dynamic addressing */
+ __u64 name;
+ /*
+ * pgn:
+ * 8bit: PS in PDU2 case, else 0
+ * 8bit: PF
+ * 1bit: DP
+ * 1bit: reserved
+ */
+ __u32 pgn;
+
+ /* 1byte address */
+ __u8 addr;
+ } j1939;
+
+ /* reserved for future CAN protocols address information */
+ } can_addr;
+};
+
+/**
+ * struct can_filter - CAN ID based filter in can_register().
+ * @can_id: relevant bits of CAN ID which are not masked out.
+ * @can_mask: CAN mask (see description)
+ *
+ * Description:
+ * A filter matches, when
+ *
+ * <received_can_id> & mask == can_id & mask
+ *
+ * The filter can be inverted (CAN_INV_FILTER bit set in can_id) or it can
+ * filter for error frames (CAN_ERR_FLAG bit set in mask).
+ */
+struct can_filter {
+ canid_t can_id;
+ canid_t can_mask;
+};
+
+#define CAN_INV_FILTER 0x20000000U /* to be set in can_filter.can_id */
+
+#endif /* CAN_H */
diff --git a/include/linux/can/j1939.h b/include/linux/can/j1939.h
new file mode 100644
index 0000000..fa62562
--- /dev/null
+++ b/include/linux/can/j1939.h
@@ -0,0 +1,93 @@
+/*
+ * j1939.h
+ *
+ */
+
+#ifndef _J1939_H_
+#define _J1939_H_
+
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/can.h>
+
+#define J1939_NO_ADDR 0xff
+#define J1939_NO_NAME 0
+#define J1939_NO_PGN 0x7ffff
+/*
+ * J1939 Parameter Group Number
+ *
+ * bit 0-7 : PDU Specific (PS)
+ * bit 8-15 : PDU Format (PF)
+ * bit 16 : Data Page (DP)
+ * bit 17 : Reserved (R)
+ * bit 19-31 : set to zero
+ */
+typedef __u32 pgn_t;
+
+/*
+ * J1939 Priority
+ *
+ * bit 0-2 : Priority (P)
+ * bit 3-7 : set to zero
+ */
+typedef __u8 priority_t;
+
+/*
+ * J1939 NAME
+ *
+ * bit 0-20 : Identity Number
+ * bit 21-31 : Manufacturer Code
+ * bit 32-34 : ECU Instance
+ * bit 35-39 : Function Instance
+ * bit 40-47 : Function
+ * bit 48 : Reserved
+ * bit 49-55 : Vehicle System
+ * bit 56-59 : Vehicle System Instance
+ * bit 60-62 : Industry Group
+ * bit 63 : Arbitrary Address Capable
+ */
+typedef __u64 name_t;
+
+/*
+ * J1939 socket options
+ */
+#define SOL_CAN_J1939 (SOL_CAN_BASE + CAN_J1939)
+enum {
+ SO_J1939_FILTER = 1, /* set filters */
+ SO_J1939_PROMISC = 2, /* set/clr promiscuous mode */
+ SO_J1939_RECV_OWN = 3,
+ SO_J1939_RECV_DEST = 4, /* set/clr attach dest control message */
+ SO_J1939_RECV_PRIO = 5,
+ SO_J1939_SEND_PRIO = 6,
+ SO_J1939_DEST_MASK = 7, /* mask names in connect() & sendto() */
+};
+
+#define SCM_J1939_DEST SO_J1939_RECV_DEST
+#define SCM_J1939_PRIO SO_J1939_RECV_PRIO
+
+struct j1939_filter {
+ name_t name;
+ name_t name_mask;
+ __u8 addr;
+ __u8 addr_mask;
+ pgn_t pgn;
+ pgn_t pgn_mask;
+};
+
+/*
+ * RTNETLINK
+ */
+enum {
+ IFLA_J1939_UNSPEC,
+ IFLA_J1939_ENABLE,
+ IFLA_J1939_MAX,
+};
+
+enum {
+ IFA_J1939_UNSPEC,
+ IFA_J1939_ADDR,
+ IFA_J1939_NAME,
+ IFA_J1939_MAX,
+};
+
+#endif /* _J1939_H_ */
diff --git a/include/utils.h b/include/utils.h
index 47f8e07..cbd249a 100644
--- a/include/utils.h
+++ b/include/utils.h
@@ -95,8 +95,15 @@ extern __u8* hexstring_a2n(const char *str, __u8 *buf, int blen);
extern const char *format_host(int af, int len, const void *addr,
char *buf, int buflen);
-extern const char *rt_addr_n2a(int af, int len, const void *addr,
+/* 'address with protocol' n2a */
+extern const char *rt_addrpr_n2a(int af, int protocol, int len, const void *addr,
char *buf, int buflen);
+static inline const char *rt_addr_n2a(int af, int len, const void *addr,
+ char *buf, int buflen)
+{
+ return rt_addrpr_n2a(af, 0, len, addr, buf, buflen);
+
+}
void missarg(const char *) __attribute__((noreturn));
void invarg(const char *, const char *) __attribute__((noreturn));
@@ -111,6 +118,16 @@ int dnet_pton(int af, const char *src, void *addr);
const char *ipx_ntop(int af, const void *addr, char *str, size_t len);
int ipx_pton(int af, const char *src, void *addr);
+/* j1939 */
+extern const char *j1939_ntop(int af, const void *addr, size_t vlen,
+ char *str, size_t len);
+extern const char *j1939_link_attrtop(struct rtattr *nla);
+
+extern int j1939_addr_args(int argc, char *argv[],
+ struct nlmsghdr *msg, int msg_size);
+extern int j1939_link_args(int argc, char *argv[],
+ struct nlmsghdr *msg, int msg_size);
+
extern int __iproute2_hz_internal;
extern int __get_hz(void);
diff --git a/ip/ip.c b/ip/ip.c
index b127d57..50fcb1c 100644
--- a/ip/ip.c
+++ b/ip/ip.c
@@ -46,7 +46,7 @@ static void usage(void)
"where OBJECT := { link | addr | addrlabel | route | rule | neigh | ntable |\n"
" tunnel | tuntap | maddr | mroute | mrule | monitor | xfrm }\n"
" OPTIONS := { -V[ersion] | -s[tatistics] | -d[etails] | -r[esolve] |\n"
-" -f[amily] { inet | inet6 | ipx | dnet | link } |\n"
+" -f[amily] { inet | inet6 | ipx | dnet | link | can} |\n"
" -l[oops] { maximum-addr-flush-attempts } |\n"
" -o[neline] | -t[imestamp] | -b[atch] [filename] |\n"
" -rc[vbuf] [size]}\n");
@@ -181,6 +181,8 @@ int main(int argc, char **argv)
preferred_family = AF_PACKET;
else if (strcmp(argv[1], "ipx") == 0)
preferred_family = AF_IPX;
+ else if (strcmp(argv[1], "can") == 0)
+ preferred_family = AF_CAN;
else if (strcmp(argv[1], "help") == 0)
usage();
else
diff --git a/ip/ipaddress.c b/ip/ipaddress.c
index a1f78b9..d7ee83a 100644
--- a/ip/ipaddress.c
+++ b/ip/ipaddress.c
@@ -27,6 +27,7 @@
#include <linux/netdevice.h>
#include <linux/if_arp.h>
#include <linux/sockios.h>
+#include <linux/can.h>
#include "rt_names.h"
#include "utils.h"
@@ -69,6 +70,8 @@ static void usage(void)
fprintf(stderr, "IFADDR := PREFIX | ADDR peer PREFIX\n");
fprintf(stderr, " [ broadcast ADDR ] [ anycast ADDR ]\n");
fprintf(stderr, " [ label STRING ] [ scope SCOPE-ID ]\n");
+ fprintf(stderr, " | j1939 J1939IFADDR\n");
+ fprintf(stderr, " \n");
fprintf(stderr, "SCOPE-ID := [ host | link | global | NUMBER ]\n");
fprintf(stderr, "FLAG-LIST := [ FLAG-LIST ] FLAG\n");
fprintf(stderr, "FLAG := [ permanent | dynamic | secondary | primary |\n");
@@ -78,6 +81,10 @@ static void usage(void)
fprintf(stderr, "CONFFLAG := [ home | nodad ]\n");
fprintf(stderr, "LIFETIME := [ valid_lft LFT ] [ preferred_lft LFT ]\n");
fprintf(stderr, "LFT := forever | SECONDS\n");
+ fprintf(stderr, " \n");
+ fprintf(stderr, "J1939IFADDR := [SA] [ name NODENAME ]\n");
+ fprintf(stderr, "SA := U8\n");
+ fprintf(stderr, "NODENAME := U64\n");
exit(-1);
}
@@ -426,6 +433,19 @@ int print_linkinfo(const struct sockaddr_nl *who,
}
fprintf(fp, "\n");
+
+ if (do_link && tb[IFLA_AF_SPEC]) {
+ struct rtattr *af[AF_MAX];
+
+ parse_rtattr_nested(af, AF_MAX, tb[IFLA_AF_SPEC]);
+ if (af[AF_CAN]) {
+ struct rtattr *prot[CAN_NPROTO];
+
+ parse_rtattr_nested(prot, CAN_NPROTO, af[AF_CAN]);
+ if (prot[CAN_J1939])
+ fprintf(fp, " %s\n", j1939_link_attrtop(prot[CAN_J1939]));
+ }
+ }
fflush(fp);
return 0;
}
@@ -450,6 +470,13 @@ static int set_lifetime(unsigned int *lifetime, char *argv)
return 0;
}
+static const int af_use_prefix[AF_MAX] = {
+ [AF_INET] = 1,
+ [AF_INET6] = 1,
+ [AF_DECnet] = 1,
+ [AF_IPX] = 1,
+};
+
int print_addrinfo(const struct sockaddr_nl *who, struct nlmsghdr *n,
void *arg)
{
@@ -457,6 +484,7 @@ int print_addrinfo(const struct sockaddr_nl *who, struct nlmsghdr *n,
struct ifaddrmsg *ifa = NLMSG_DATA(n);
int len = n->nlmsg_len;
int deprecated = 0;
+ int protocol = 0;
/* Use local copy of ifa_flags to not interfere with filtering code */
unsigned int ifa_flags;
struct rtattr * rta_tb[IFA_MAX+1];
@@ -476,10 +504,12 @@ int print_addrinfo(const struct sockaddr_nl *who, struct nlmsghdr *n,
parse_rtattr(rta_tb, IFA_MAX, IFA_RTA(ifa), n->nlmsg_len - NLMSG_LENGTH(sizeof(*ifa)));
- if (!rta_tb[IFA_LOCAL])
- rta_tb[IFA_LOCAL] = rta_tb[IFA_ADDRESS];
- if (!rta_tb[IFA_ADDRESS])
- rta_tb[IFA_ADDRESS] = rta_tb[IFA_LOCAL];
+ if (af_use_prefix[ifa->ifa_family]) {
+ if (!rta_tb[IFA_LOCAL])
+ rta_tb[IFA_LOCAL] = rta_tb[IFA_ADDRESS];
+ if (!rta_tb[IFA_ADDRESS])
+ rta_tb[IFA_ADDRESS] = rta_tb[IFA_LOCAL];
+ }
if (filter.ifindex && filter.ifindex != ifa->ifa_index)
return 0;
@@ -541,38 +571,64 @@ int print_addrinfo(const struct sockaddr_nl *who, struct nlmsghdr *n,
fprintf(fp, " dnet ");
else if (ifa->ifa_family == AF_IPX)
fprintf(fp, " ipx ");
+ else if (ifa->ifa_family == AF_CAN) {
+ /* ifa->ifa_prefixlen is abused for protocol number */
+ const char *sprotocol;
+ char num[16];
+
+ /* 1st: set protocol, as this is rather tricky */
+ protocol = ifa->ifa_prefixlen;
+
+ /* 2nd: set label */
+ switch (protocol) {
+ case CAN_J1939:
+ sprotocol = "j1939";
+ break;
+ default:
+ sprintf(num, "%i", ifa->ifa_prefixlen);
+ sprotocol = num;
+ break;
+ }
+ fprintf(fp, " can-%s ", sprotocol);
+ }
else
fprintf(fp, " family %d ", ifa->ifa_family);
if (rta_tb[IFA_LOCAL]) {
- fprintf(fp, "%s", rt_addr_n2a(ifa->ifa_family,
+ fprintf(fp, "%s", rt_addrpr_n2a(ifa->ifa_family, protocol,
RTA_PAYLOAD(rta_tb[IFA_LOCAL]),
RTA_DATA(rta_tb[IFA_LOCAL]),
abuf, sizeof(abuf)));
if (rta_tb[IFA_ADDRESS] == NULL ||
memcmp(RTA_DATA(rta_tb[IFA_ADDRESS]), RTA_DATA(rta_tb[IFA_LOCAL]), 4) == 0) {
- fprintf(fp, "/%d ", ifa->ifa_prefixlen);
} else {
- fprintf(fp, " peer %s/%d ",
- rt_addr_n2a(ifa->ifa_family,
+ fprintf(fp, " peer %s",
+ rt_addrpr_n2a(ifa->ifa_family, protocol,
RTA_PAYLOAD(rta_tb[IFA_ADDRESS]),
RTA_DATA(rta_tb[IFA_ADDRESS]),
- abuf, sizeof(abuf)),
- ifa->ifa_prefixlen);
+ abuf, sizeof(abuf)));
}
+ if (af_use_prefix[ifa->ifa_family])
+ fprintf(fp, "/%d", ifa->ifa_prefixlen);
+ fprintf(fp, " ");
+ } else if (rta_tb[IFA_ADDRESS]) {
+ fprintf(fp, "peer %s ", rt_addrpr_n2a(ifa->ifa_family, protocol,
+ RTA_PAYLOAD(rta_tb[IFA_ADDRESS]),
+ RTA_DATA(rta_tb[IFA_ADDRESS]),
+ abuf, sizeof(abuf)));
}
if (rta_tb[IFA_BROADCAST]) {
fprintf(fp, "brd %s ",
- rt_addr_n2a(ifa->ifa_family,
+ rt_addrpr_n2a(ifa->ifa_family, protocol,
RTA_PAYLOAD(rta_tb[IFA_BROADCAST]),
RTA_DATA(rta_tb[IFA_BROADCAST]),
abuf, sizeof(abuf)));
}
if (rta_tb[IFA_ANYCAST]) {
fprintf(fp, "any %s ",
- rt_addr_n2a(ifa->ifa_family,
+ rt_addrpr_n2a(ifa->ifa_family, protocol,
RTA_PAYLOAD(rta_tb[IFA_ANYCAST]),
RTA_DATA(rta_tb[IFA_ANYCAST]),
abuf, sizeof(abuf)));
@@ -1103,12 +1159,18 @@ static int ipaddr_modify(int cmd, int flags, int argc, char **argv)
req.ifa.ifa_flags |= IFA_F_HOMEADDRESS;
} else if (strcmp(*argv, "nodad") == 0) {
req.ifa.ifa_flags |= IFA_F_NODAD;
+ } else if (matches(*argv, "j1939") == 0) {
+ int ret;
+
+ ret = j1939_addr_args(argc, argv, &req.n, sizeof(req));
+ if (ret < 0)
+ return ret;
+ argc -= ret;
+ argv += ret;
} else {
if (strcmp(*argv, "local") == 0) {
NEXT_ARG();
}
- if (matches(*argv, "help") == 0)
- usage();
if (local_len)
duparg2("local", *argv);
lcl_arg = *argv;
@@ -1214,8 +1276,9 @@ int do_ipaddr(int argc, char **argv)
return ipaddr_list_or_flush(argc-1, argv+1, 0);
if (matches(*argv, "flush") == 0)
return ipaddr_list_or_flush(argc-1, argv+1, 1);
- if (matches(*argv, "help") == 0)
+ if (matches(*argv, "help") == 0) {
usage();
+ }
fprintf(stderr, "Command \"%s\" is unknown, try \"ip addr help\".\n", *argv);
exit(-1);
}
diff --git a/ip/iplink.c b/ip/iplink.c
index 48c0254..cd8d906 100644
--- a/ip/iplink.c
+++ b/ip/iplink.c
@@ -27,6 +27,7 @@
#include <string.h>
#include <sys/ioctl.h>
#include <linux/sockios.h>
+#include <linux/can.h>
#include "rt_names.h"
#include "utils.h"
@@ -73,6 +74,7 @@ void iplink_usage(void)
fprintf(stderr, " [ rate TXRATE ] ] \n");
fprintf(stderr, " [ master DEVICE ]\n");
fprintf(stderr, " [ nomaster ]\n");
+ fprintf(stderr, " [ j1939 { on | off } ]\n");
fprintf(stderr, " ip link show [ DEVICE | group GROUP ]\n");
if (iplink_have_newlink()) {
@@ -402,6 +404,12 @@ int iplink_parse(int argc, char **argv, struct iplink_req *req,
duparg("group", *argv);
if (rtnl_group_a2n(group, *argv))
invarg("Invalid \"group\" value\n", *argv);
+ } else if (matches(*argv, "j1939") == 0) {
+ ret = j1939_link_args(argc, argv, &req->n, sizeof(*req));
+ if (ret < 0)
+ return ret;
+ argc -= ret;
+ argv += ret;
} else {
if (strcmp(*argv, "dev") == 0) {
NEXT_ARG();
diff --git a/lib/j1939.c b/lib/j1939.c
new file mode 100644
index 0000000..157a8ce
--- /dev/null
+++ b/lib/j1939.c
@@ -0,0 +1,153 @@
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <endian.h>
+#include <linux/can/j1939.h>
+
+#include "utils.h"
+
+#ifndef htobe64
+# if __BYTE_ORDER == __LITTLE_ENDIAN
+# define htobe64(x) __bswap_64 (x)
+# define htole64(x) (x)
+# define be64toh(x) __bswap_64 (x)
+# define le64toh(x) (x)
+# else
+# define htobe64(x) (x)
+# define htole64(x) __bswap_64 (x)
+# define be64toh(x) (x)
+# define le64toh(x) __bswap_64 (x)
+# endif
+#endif
+/*
+ * print J1939 name
+ * for use from rt_addr_n2a
+ */
+const char *j1939_ntop(int af, const void *vaddr, size_t vlen,
+ char *str, size_t len)
+{
+ struct rtattr *tb[IFA_J1939_MAX];
+ int strdone = 0;
+
+ /* cast vaddr to non-const pointer */
+ parse_rtattr(tb, IFA_J1939_MAX-1, (void *)vaddr, vlen);
+ if (tb[IFA_J1939_ADDR]) {
+ strdone += sprintf(&str[strdone], "0x%02x",
+ *(uint8_t *)RTA_DATA(tb[IFA_J1939_ADDR]));
+ if (tb[IFA_J1939_NAME])
+ str[strdone++] = ' ';
+ }
+ if (tb[IFA_J1939_NAME])
+ strdone += sprintf(&str[strdone], "name %016llx",
+ (unsigned long long)be64toh(*(uint64_t *)RTA_DATA(tb[IFA_J1939_NAME])));
+ errno = 0;
+ return str;
+}
+
+/*
+ * fill an ifaddr message from program arguments
+ */
+int j1939_addr_args(int argc, char *argv[], struct nlmsghdr *msg, int msg_size)
+{
+ int saved_argc = argc;
+ struct ifaddrmsg *ifa = (void *)&msg[1];
+ struct rtattr *local;
+
+ if (ifa->ifa_family == AF_UNSPEC)
+ ifa->ifa_family = AF_CAN;
+ else {
+ fprintf(stderr, "j1939 only allowed for AF_CAN\n");
+ return -1;
+ }
+ if (!ifa->ifa_prefixlen)
+ ifa->ifa_prefixlen = CAN_J1939;
+ else {
+ fprintf(stderr, "CAN protocol %i already specified",
+ ifa->ifa_prefixlen);
+ return -1;
+ }
+ NEXT_ARG();
+ /* j1939 SA & NAME never need to be specified together */
+ if (matches(*argv, "name") == 0) {
+ uint64_t name;
+
+ NEXT_ARG();
+ name = htobe64(strtoull(*argv, 0, 16));
+ if (!name) {
+ fprintf(stderr, "0 name is not valid\n");
+ return -1;
+ }
+ local = addattr_nest(msg, msg_size, IFA_LOCAL);
+ addattr_l(msg, msg_size, IFA_J1939_NAME, &name, sizeof(name));
+ addattr_nest_end(msg, local);
+ } else {
+ unsigned int laddr;
+ uint8_t addr;
+
+ addr = laddr = strtoul(*argv, 0, 0);
+ if (laddr >= 0xfe) {
+ fprintf(stderr, "address '%s' not valid\n", *argv);
+ return -1;
+ }
+ local = addattr_nest(msg, msg_size, IFA_LOCAL);
+ addattr_l(msg, msg_size, IFA_J1939_ADDR, &addr, sizeof(addr));
+ addattr_nest_end(msg, local);
+ }
+
+ return saved_argc - argc;
+}
+
+/*
+ * fill an link_af message from program arguments
+ */
+int j1939_link_args(int argc, char *argv[], struct nlmsghdr *msg, int msg_size)
+{
+ int saved_argc = argc;
+ struct rtattr *afspec, *can, *j1939;
+ uint8_t enable;
+
+ NEXT_ARG();
+ if (strcmp(*argv, "on") == 0) {
+ enable = 1;
+ } else if (strcmp(*argv, "off") == 0) {
+ enable = 0;
+ } else {
+ enable = 1;
+ /* revert arguments */
+ ++argc;
+ --argv;
+ }
+
+ afspec = addattr_nest(msg, msg_size, IFLA_AF_SPEC);
+ can = addattr_nest(msg, msg_size, AF_CAN);
+ j1939 = addattr_nest(msg, msg_size, CAN_J1939);
+ addattr_l(msg, msg_size, IFLA_J1939_ENABLE, &enable, sizeof(enable));
+ addattr_nest_end(msg, j1939);
+ addattr_nest_end(msg, can);
+ addattr_nest_end(msg, afspec);
+
+ return saved_argc - argc;
+}
+
+/*
+ * process the returned IFLA_AF_SPEC/AF_CAN/CAN_J1939 attribute
+ */
+const char *j1939_link_attrtop(struct rtattr *nla)
+{
+ static char str[32];
+ int pos;
+ struct rtattr *tb[IFLA_J1939_MAX];
+
+ pos = 0;
+ str[0] = 0;
+ parse_rtattr_nested(tb, IFLA_J1939_MAX-1, nla);
+ if (tb[IFLA_J1939_ENABLE]) {
+ uint8_t *u8ptr;
+
+ u8ptr = RTA_DATA(tb[IFLA_J1939_ENABLE]);
+ pos += sprintf(&str[pos], "j1939 %s", *u8ptr ? "on" : "off");
+ }
+ return str;
+}
+
diff --git a/lib/utils.c b/lib/utils.c
index 1b42222..570e43f 100644
--- a/lib/utils.c
+++ b/lib/utils.c
@@ -25,6 +25,7 @@
#include <linux/pkt_sched.h>
#include <time.h>
#include <sys/time.h>
+#include <linux/can.h>
#include "utils.h"
@@ -499,7 +500,8 @@ int __get_user_hz(void)
return sysconf(_SC_CLK_TCK);
}
-const char *rt_addr_n2a(int af, int len, const void *addr, char *buf, int buflen)
+const char *rt_addrpr_n2a(int af, int protocol, int len, const void *addr,
+ char *buf, int buflen)
{
switch (af) {
case AF_INET:
@@ -513,6 +515,11 @@ const char *rt_addr_n2a(int af, int len, const void *addr, char *buf, int buflen
memcpy(dna.a_addr, addr, 2);
return dnet_ntop(af, &dna, buf, buflen);
}
+ case AF_CAN:
+ switch (protocol) {
+ case CAN_J1939:
+ return j1939_ntop(af, addr, len, buf, buflen);
+ }
default:
return "???";
}
diff --git a/man/man8/ip.8 b/man/man8/ip.8
index c5248ef..eab2fd4 100644
--- a/man/man8/ip.8
+++ b/man/man8/ip.8
@@ -23,7 +23,7 @@ ip \- show / manipulate routing, devices, policy routing and tunnels
\fB\-s\fR[\fItatistics\fR] |
\fB\-r\fR[\fIesolve\fR] |
\fB\-f\fR[\fIamily\fR] {
-.BR inet " | " inet6 " | " ipx " | " dnet " | " link " } | "
+.BR inet " | " inet6 " | " ipx " | " dnet " | " link " | " can " } | "
\fB\-o\fR[\fIneline\fR] }
.ti -8
@@ -103,6 +103,8 @@ ip \- show / manipulate routing, devices, policy routing and tunnels
.IR DEVICE
.br
.B nomaster
+.br
+.BR j1939 " { " on " | " off " }"
.ti -8
.B ip link show
@@ -135,7 +137,9 @@ ip \- show / manipulate routing, devices, policy routing and tunnels
.B label
.IR STRING " ] [ "
.B scope
-.IR SCOPE-ID " ]"
+.IR SCOPE-ID " ] | "
+.B j1939
+.IR IFADDRJ1939 " ] "
.ti -8
.IR SCOPE-ID " := "
@@ -151,6 +155,15 @@ ip \- show / manipulate routing, devices, policy routing and tunnels
tentative " | " deprecated " | " dadfailed " | " temporary " ]"
.ti -8
+.IR J1939IFADDR " := [ " SA " ] [ "
+.B name
+.IR " NODENAME " ]
+.br
+.IR SA " := " U8
+.br
+.IR NODENAME " := " U64
+
+.ti -8
.BR "ip addrlabel" " { " add " | " del " } " prefix
.BR PREFIX " [ "
.B dev
@@ -1067,6 +1080,9 @@ set master device of the device (enslave device).
.TP
.BI nomaster
unset master device of the device (release device).
+.BR "j1939 on " or "j1939 off"
+Enable or disable SAE J1939 on the device. This will only
+work when the device is a CAN device.
.PP
.B Warning:
^ permalink raw reply related
* [PATCH v4 4/5] can-j1939: add documentation
From: Kurt Van Dijck @ 2011-04-27 9:00 UTC (permalink / raw)
To: socketcan-core-0fE9KPoRgkgATYTw5x5z8w,
netdev-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20110427085330.GA757-ozGf4kBk5synFtIcQ8t7k3L8HoS0Hn3T@public.gmane.org>
Add documentation of the SAE J1939 protocol stack
Signed-off-by: Kurt Van Dijck <kurt.van.dijck-/BeEPy95v10@public.gmane.org>
---
Documentation/networking/j1939.txt | 576 ++++++++++++++++++++++++++++++++++++
MAINTAINERS | 8 +
2 files changed, 584 insertions(+), 0 deletions(-)
create mode 100644 Documentation/networking/j1939.txt
diff --git a/Documentation/networking/j1939.txt b/Documentation/networking/j1939.txt
new file mode 100644
index 0000000..ef0a963
--- /dev/null
+++ b/Documentation/networking/j1939.txt
@@ -0,0 +1,576 @@
+============================================================================
+
+j1939.txt
+
+Readme file for the J1939 Protocol
+
+This file contains
+
+ 1 Overview / What is j1939
+ 1.1 specifications used
+
+ 2 Motivation
+
+ 3 J1939 concepts
+ 3.1 socket type
+ 3.2 addressing
+ 3.3 priority
+ 3.4 PGN
+ 3.5 filtering
+ 3.6 destinations with dynamic address
+
+ 4 How to use J1939
+ 4.1 rtnetlink interface
+ 4.2 API calls
+ 4.2.1 Message flags during sendmsg
+ 4.2.2 SCM_J1939_DEST_ADDR & SCM_J1939_DEST_NAME
+ 4.2.3 SCM_J1939_PRIORITY
+ 4.3 Dynamic addressing
+ 4.4 Send Examples
+ 4.4.1 Static address
+ 4.4.2 Dynamic address
+ 4.4.3 Mixed mode
+
+ 5 socket options
+ 5.1 SO_J1939_FILTER
+ 5.2 SO_J1939_PROMISC
+ 5.3 SO_J1939_RECV_OWN
+ 5.4 SO_J1939_SEND_PRIO
+
+ 6 can-j1939 procfs interface
+ 6.1 /proc/net/can-j1939/ecu
+ 6.2 /proc/net/can-j1939/filter
+ 6.3 /proc/net/can-j1939/sock
+ 6.4 /proc/net/can-j1939/transport
+
+ 7 can-j1939 SYSCTL
+ 7.1 /proc/sys/net/can-j1939/transport_max_payload_in_bytes
+ 7.2 /proc/sys/net/can-j1939/transport_cts_nr_of_frames
+ 7.3 /proc/sys/net/can-j1939/transport_tx_retry_ms
+
+ 8 Credits
+
+============================================================================
+
+1. Introduction
+--------------------------------
+
+ SAE J1939 defines a higher layer protocol on CAN. It implements a more
+ sophisticated addressing scheme and extends the maximum packet size above
+ 8 bytes. Several derived specifications exists, which differ from the
+ original j1939 on the application level, like MilCAN, NMEA2000 and
+ especially ISO-11783 (ISOBUS). This last one specifies the so-called ETP
+ (Extended Transport Protocol) which is has been included in this
+ implementation. This inclusion results in a maximum packet size of
+ ((2^24)-1)*7 bytes
+
+
+1.1 specifications used
+
+ SAE J1939-21 : data link layer
+ SAE J1939-81 : network management
+ ISO 11783-6 : Virtual Terminal (Extended Transport Protocol)
+
+
+2. Motivation
+--------------------------------
+
+ Given the fact there's something like SocketCAN with an API similar to BSD
+ sockets, we found some reasons to justify a kernel implementation for the
+ addressing and transport methods used by J1939.
+
+ * addressing:
+ When a process on an ECU communicates via j1939, it should not necessarily
+ know its source address. Although at least 1 process per ECU should know
+ the source address. Other processes should be able to reuse that address.
+ This way, address parameters for different processes cooperating for the
+ same ECU, are not duplicated.
+ This way of working is closely related to the unix concept where programs
+ do just 1 thing, and do it well.
+
+ * dynamic addressing:
+ Address Claiming in J1939 is time critical. Furthermore data transport
+ should be handled properly during the address negotiation. Putting these
+ functionality in the kernel eliminates this functionality as a requirement
+ for _every_ userspace process that communicates via J1939. This results in
+ a consistent J1939 bus with proper addressing.
+
+ * transport:
+ Both TP & ETP reuse some PGN's to relay big packets over them. Different
+ processes may thus use the same TP & ETP PGN's without actually knowing it.
+ The individual TP & ETP sessions _must_ be serialized (synchronised)
+ between different processes. The kernel solves this problem properly, and
+ eliminates the serialisation (synchronisation) as a requirement for
+ _every_ userspace process that communicates via J1939.
+
+ J1939 defines some other features (relaying, gateway, Fast Packet transport,
+ ...). In-kernel code for these would not contribute to protocol stability.
+ Therefore, these parts are left to userspace.
+
+ The j1939 sockets operate on CAN network devices (see SocketCAN). Any j1939
+ userspace library operating on CAN raw sockets will still operate properly.
+ Since such library does not communicate with the in-kernel implementation,
+ care must be taken that these 2 do not interfere. In practice, this means
+ they cannot share ECU addresses. A single ECU (or virtual ECU) address is
+ used by the library exclusively, or by the in-kernel system exclusively.
+
+
+3. J1939 concepts
+--------------------------------
+
+3.1 PGN
+
+ The PGN (Parameter Group Number) is a number to identify a packet. The PGN
+ is composed as follows:
+ 1 bit : Reserved Bit
+ 1 bit : Data Page
+ 8 bits : PF (PDU Format)
+ 8 bits : PS (PDU Specific)
+
+ In J1939-21, distinction is made between PDU1 Format (where PF < 240) and
+ PDU2 Format (where PF >= 240). Furthermore, when using PDU2 Format, the
+ PS-field contains a so-called Group Extension, which is part of the PGN.
+ When using PDU2 Format, the Group Extension is set in the PS-field.
+
+ On the other hand, when using PDU1 Format, the PS-field contains a so-called
+ Destination Address, which is _not_ part of the PGN. When communicating a
+ PGN from userspace to kernel (or visa versa) and PDU2 Format is used, the
+ PS-field of the PGN shall be set to zero. The Destination Address shall be
+ set elsewhere.
+
+ Regarding PGN mapping to 29-bit CAN identifier, the Destination Address
+ shall be get/set from/to the apropriate bits of the identifier by the kernel.
+
+
+3.2 addressing
+
+ Both static and dynamic addressing methods can be used.
+
+ For static addresses, no extra checks are made by the kernel, and provided
+ addresses are considered right. This responsibility is for the OEM or system
+ integrator.
+
+ For dynamic addressing, so-called Address Claiming, extra support is forseen
+ in the kernel. In J1939 any ECU is known by it's 64-bit NAME. At the moment
+ of succesfull address claim, the kernel keeps track of both NAME and source
+ address being claimed. This serves as a base for filter schemes. By default,
+ packets with a destination that is not locally, will be rejected soon after
+ reception.
+
+ Mixed mode packets (from a static to a dynamic address or vice versa) are
+ allowed. The BSD sockets define seperate API calls for getting/setting the
+ local & remote address and are applicable for J1939 sockets.
+
+
+3.3 Filtering
+
+ Similar to SocketCAN, j1939 defines filters per socket that a user can set
+ in order to receive a subset of the j1939 traffic. Filtering can base on
+ * SA
+ * NAME
+ * PGN
+
+ There is a semantic difference with SocketCAN with regard to filtering.
+ When multiple filters are in place for a single socket, and a packet comes
+ in that matches several of those filters, the packet is only received once
+ for that socket.
+ The rationale behind this difference originates in the filter capabilities.
+ Where SocketCAN filters on only 1 orthogonal (can id), J1939 can filter
+ on 3 orthogonal properties (sa, name, pgn).
+
+ When a filter on the SA is set, j1939 traffic with a matching SA, but with
+ its NAME set (aka having claimed SA successfully) will match, although
+ the filter would not match its NAME.
+
+ Filtering on priority is _not_ supported.
+
+
+4. How to use J1939
+--------------------------------
+
+4.1 rtnetlink interface
+
+ Per default j1939 is not active. Specifying can_ifindex != 0 in bind(2)
+ or connect(2) needs an active j1939 on that interface. You must have done
+ $ ip link set canX j1939 on
+ on that interface.
+
+ $ ip link set canX j1939 down
+ disables j1939 on canX.
+
+ Assigning addresses is done via
+ $ ip addr add dev canX j1939 0xXX
+ statically or
+ $ ip addr add dev canX j1939 name 0xXX
+ dynamically. In the latter case, address claiming must take place
+ before other traffic can leave.
+
+ Removing addresses is done similarly via
+ $ ip addr del dev canX j1939 0xXX
+ $ ip addr del dev canX j1939 name 0xXX
+
+ A static address cannot be assigned together with a 64bit name.
+
+4.2 API calls
+
+ Like TCP/IP and CAN, you first need to open a socket for communicating over a
+ CAN network. To use j1939, include <include/linux/j1939.h>. From there,
+ <include/linux/can.h> will be included too.
+ To open a socket, you would write
+
+ s = socket(PF_CAN, SOCK_DGRAM, CAN_J1939);
+
+ J1939 does use SOCK_DGRAM sockets. In the j1939 specification, connections are
+ mentioned in the context of transport protocol sessions. These still deliver
+ packets to the other end (using several CAN packets).
+ SOCK_STREAM is never appropriate.
+
+ After the successful creation of the socket, you would normally use the
+ bind(2) and/or connect(2) system call to bind the socket to a CAN interface
+ (which is different from TCP/IP due to different addressing) After binding
+ and/or connecting the socket, you can read(2) and write(2) from/to the socket
+ or use send(2), sendto(2), sendmsg(2) and the recv* counterpart operations on
+ the socket as usual. There are also J1939 specific socket options described
+ below.
+
+ In order to send data, a bind(2) must have succeeded. bind(2) assigns a local
+ address to a socket. For this to succeed, you can only choose addresses
+ that have been assigned earlier (see 4.1). When an empty address is assigned
+ (ie. SA=0xff && name=0), a default is taken for the device that is bound to.
+
+ Different from CAN is that the payload data is just the data that get send,
+ without it's header info. The header info is derived from the sockaddr
+ supplied to bind(2), connect(2), sendto(2) and recvfrom(2). A write(2) with
+ size 4 will result in a packet with 4 bytes.
+
+ The sockaddr structure has extensions for use with j1939 as specified below:
+ struct sockaddr_can {
+ sa_family_t can_family;
+ int can_ifindex;
+ union {
+ struct {
+ __u64 name;
+ __u32 pgn;
+ __u8 addr;
+ } j1939;
+ } can_addr;
+ }
+
+ can_family & can_ifindex serve the same purpose as for other SocketCAN sockets.
+
+ can_addr.j1939.pgn specifies the PGN (max 0x3ffff). Individual bits are
+ specified above.
+
+ can_addr.j1939.name contains the 64-bit J1939 NAME.
+
+ can_addr.j1939.addr contains the source address.
+
+ When sending data, the source address is applied as follows: If
+ can_addr.j1939.name != 0 the NAME is looked up by the kernel and the
+ corresponding Source Address is used. If can_addr.j1939.name == 0,
+ can_addr.j1939.addr is used.
+
+ After a bind(2), the local address is assigned, i.e. the source address.
+ After a connect(2), the remote address is assigned, i.e. the destination
+ address.
+
+ Both write(2) and send(2) will send a packet with local address from bind,
+ remote address from connect(2). When the address was not set, a broadcast is
+ sent. The PGN is used from bind(2) or overruled with sendto(2), which will
+ override the destination address when valid, and the PGN when valid.
+
+ Both read(2) and recv(2) will receive packets matching the sockets filters.
+ recvfrom(2) will receive these packets with originator's address.
+
+ When creating a socket, reasonable defaults have been set. Some options can be
+ modified with setsockopt(2) & getsockopt(2).
+
+4.2.1 Message flags during sendmsg
+
+ send(2), sendto(2) and sendmsg(2) take a 'flags' argument. J1939 interpretes
+ these flags during outgoing traffic:
+
+ * MSG_DONTWAIT determines nonblocking operation. When a packet must wait for
+ any reason, -EAGAIN is returned.
+
+ * MSG_SYN
+ Packets flagged with MSG_SYN will wait for all pending packets on a socket
+ to be sent before trying to send. This means that if a socket just started
+ a Transport Protocol session, a packet with MSG_SYN will wait for that
+ session to complete before proceeding.
+ Traffic without MSG_SYN (on that very same socket) will still continue.
+
+4.2.2 SCM_J1939_DEST_ADDR & SCM_J1939_DEST_NAME
+
+ Different received j1939 packets could have had different destionations:
+ - broadcast packet, i.e. no destination address
+ - destination address that matches the sockets local address
+ - destination address that matches _a_ local address on the system, and the
+ socket had no local address defined.
+ - SO_J1939_PROMISC was set
+
+ The destination address & destination name (if applicable) are attached
+ to the msghdr in the recvmsg(2) call. It can be extracted using cmsg(3) macros,
+ with cmsg_level == SOL_J1939 && cmsg_type == SCM_J1939_DEST_ADDR
+ or SCM_J1939_DEST_NAME. The returned data is a uint8_t/uint64_t.
+
+4.2.3 SCM_J1939_PRIORITY
+
+ Attached to the msghdr is also the packet's priority on the bus. This is a
+ uint8_t, packed as cmsg_type == SCM_J1939_PRIORITY.
+
+4.3 Dynamic Addressing
+
+ Distinction has to be made in and using the claimed address and doing an
+ address claim. To use an already claimed address, one has to fill in the
+ j1939.name member and provide it to bind(2). If the name had claimed an
+ address earlier, all further PGN's being sent will use that address. And the
+ j1939.addr member will be ignored.
+
+ An exception on this is pgn 0x0ee00. This is the "Address Claim/Cannot Claim
+ Address" message and when the kernel will use the j1939.addr member for that
+ pgn if necessary.
+
+ To claim an address, bind(2) with:
+ j1939.pgn set to 0x0ee00
+ j1939.addr set to the desired Source Address.
+ j1939.name set to the NAME you want the Source Address to claim to.
+
+ Afterwards do a write(2) with data set to the NAME (Little Endian). If the
+ NAME provided, does not match the j1939.name provided to bind(2), EPROTO
+ will be returned. One might use sendto(2) also to send the Addres Claim. In
+ that case, the j1939.addr member must be set to the broadcast address (255)
+ and the j1939.pgn must be set to 0x0ee00. If This combination is not given,
+ EPROTO is returned.
+
+ If no-one else contest the address claim within 250ms after transmission, the
+ kernel marks the NAME-SA assignment as valid. The valid assignment will be
+ kept, among other valid NAME-SA assignments. From that point, any socket
+ bound to the NAME can send packets.
+
+ If another ECU claims the address, the kernel will mark the NAME-SA expired.
+ No socket bound to the NAME can send packets (other than address claims).
+ To claim another address, some socket bound to NAME, must bind(2) again,
+ but with only j1939.addr changed to the new SA, and must then send a
+ valid address claim packet. This restarts the state machine in the kernel
+ (and any other participant on the bus) for this NAME.
+
+
+4.4 Send Examples
+
+4.4.1 Static addressing
+
+ This example will send a pgn (0x12300) from SA 0x20 to DA 0x30.
+
+ Add the address to the system:
+ $ ip addr add j1939 0x20 dev can0
+
+ Bind:
+ struct sockaddr_can addr;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.can_ifindex = ifindex("can0"); // ifindex is a substitute.
+ addr.can_addr.j1939.name = J1939_NO_NAME;
+ addr.can_addr.j1939.addr = 0x20;
+ addr.can_addr.j1939.pgn = J1939_NO_PGN;
+
+ bind(sk, (void *)&addr, sizeof(addr));
+
+ Now, the socket 'sk' is bound to the address 0x20. Since no pgn
+ was specified during bound, a pgn will be necessary during sendto() operations.
+ Alternatively, specifying addr.can_addr.j1939.pgn during bind() allows
+ for using send() & write(), since a default pgn (the pgn specified during bind())
+ can be used then.
+
+ Send:
+ struct sockaddr_can addr;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.can_addr.j1939.name = J1939_NO_NAME;
+ addr.can_addr.j1939.addr = 0x30;
+ addr.can_addr.j1939.pgn = 0x12300;
+ // addr.can_ifindex is not necessary here.
+
+ sendto(sk, data, sizeof(data), 0, (void *)&addr, sizeof(addr));
+
+4.4.2 Dynamic addressing
+
+ This example will send a pgn (0x12300) from 12345678 to 9ABCDEF
+
+ Add the name to the system:
+ $ ip addr add j1939 name 12345678 dev can0
+
+ Start an address claiming daemon (e.g. jacd)
+ $ jacd -r 0x20-0x30 12345678 can0 &
+
+ Bind:
+ struct sockaddr_can addr;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.can_ifindex = ifindex("can0"); // ifindex is a substitute.
+ addr.can_addr.j1939.name = 0x12345678ULL;
+ addr.can_addr.j1939.addr = J1939_NO_ADDR;
+ addr.can_addr.j1939.pgn = J1939_NO_PGN;
+
+ bind(sk, (void *)&addr, sizeof(addr));
+
+ Send:
+ struct sockaddr_can addr;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.can_addr.j1939.name = 0x9ABCDEFULL;
+ addr.can_addr.j1939. = J1939_NO_ADDR;
+ addr.can_addr.j1939.pgn = 0x12300;
+
+ sendto(sk, data, sizeof(data), 0, (void *)&addr, sizeof(addr));
+
+4.4.3 Mixed mode
+
+ A scenario that sends a packet from a static address to a dynamic address
+ or vice versa is called 'mixed mode' here.
+
+ Combining the setup of the static address with a sendto() to a dynamic
+ address from the above examples is legal, and implements such mixed mode
+ addressing. The same applies for the setup of the dynamic address combined
+ with the sendto() towards a dynamic address.
+
+
+5 Socket Options
+--------------------------------
+
+ j1939 sockets have some options that are configurable via setsockopt(2).
+ Each of those options is initialized with a reasonable default.
+
+
+5.1 SO_J1939_FILTER
+
+ As mentioned above, J1939 supports filtering in both NAME, Source Address
+ and PGN. All members must match.
+
+ struct j1939_filter filter = {
+ .name = ...
+ .name_mask = ...
+ .addr = ...
+ .addr_mask = ...
+ .pgn = ...
+ .pgn_mask = ...
+ }
+
+ setsockopt(s, SOL_CAN_J1939, SO_J1939_FILTER, &filter, sizeof(filter));
+
+
+5.2 SO_J1939_PROMISC
+
+ When set, j1939 will receive all packets, not just those with a destination
+ on the local system.
+ default off.
+
+ int promisc = 1; /* 0 = disabled (default), 1 = enabled */
+
+ setsockopt(s, SOL_CAN_J1939, SO_J1939_PROMISC, &promisc, sizeof(promisc));
+
+
+5.3 SO_J1939_RECV_OWN
+
+ All the sent j1939 packets are looped back in the system.
+ The reception of the j1939 packets on the same socket that was
+ sending the j1939 packet is assumed to be unwanted and therefore
+ disabled by default. This default behaviour may be changed on
+ demand:
+
+ int recv_own_msgs = 1; /* 0 = disabled (default), 1 = enabled */
+
+ setsockopt(s, SOL_CAN_J1939, SO_J1939_RECV_OWN,
+ &recv_own_msgs, sizeof(recv_own_msgs));
+
+
+5.4 SO_J1939_SEND_PRIO
+
+ To set the priority field for outgoing packets, the SO_J1939_SEND_PRIO can
+ be changed. This int field specifies the priority that will be used.
+ j1939 defines a priority between 0 and 7 inclusive,
+ with 7 the lowest priority.
+ Per default, the priority is set to 6 (conforming J1939).
+ This priority socket option operates on the same value that is modified
+ with
+
+ setsockopt(s, SOL_SOCKET, SO_PRIORITY, &pri, sizeof(pri))
+
+ socketoption, with a difference that SOL_SOCKET/SO_PRIORITY is defined with
+ 0 the lowest priority. SOL_CAN_J1939/SO_J1939_SEND_PRIO inverts this value
+ for you.
+
+
+6. /proc/net/can-j1939 Interface.
+--------------------------------
+
+ Files giving you a view on the in-kernel operation of J1939 are located at:
+ /proc/net/j1939.
+
+6.1 /proc/net/can-j1939/ecu
+
+ This file gives an overview of the known ECU's to the kernel.
+ - iface : network interface they operate on.
+ - SA : current address.
+ - name : 64bit NAME
+ - flags : 'L' = local, 'R' = remote
+
+6.2 /proc/net/can-j1939/filter
+
+6.3 /proc/net/can-j1939/sock
+
+ This file gives a list of all j1939 sockets currently open.
+ - iface : network interface
+ - flags :
+ 'b' : bound
+ 'c' : connected
+ 'P' : PROMISC
+ 'o' : RECV_OWN
+ 'd' : RECV_DEST
+ 'p' : RECV_PRIO
+ - local: [NAME],SA
+ - remote: [NAME]/MASK,DA
+ - pgn : PGN
+ - prio : priority
+ - pending : # packets pending (see MSG_SYN on 4.2.1)
+
+6.4 /proc/net/can-j1939/transport
+
+ This file shows a list of pending transport sessions
+ - iface
+ - src : XX (addr) or XXXXXXXXXXXXXXXX (name)
+ - dst : XX or XXXXXXXXXXXXXXXX or '-' (broadcast)
+ - pgn :
+ - done/total : current # transferred bytes / total
+
+
+7. /proc/sys/net/can-j1939 - SYSCTL
+--------------------------------
+
+ Via these sysctl files, some parameters of the j1939 module can be tuned.
+
+7.1 /proc/sys/net/can-j1939/transport_max_payload_in_bytes [int]
+
+ Is the maximum packet size to accept on both transmit & receive side.
+ Bigger packets will be rejected (local sender), aborted (local receiver)
+ or ignored (broadcasts & remote recievers in PROMISC).
+
+7.2 /proc/sys/net/can-j1939/transport_cts_nr_frames [int]
+
+ Controls the number of packets to allow between consecutive CTS frames
+ (default 255).
+ This number is communicated within the CTS frame from receiver to transmitter.
+ Setting this has effect on received transport sessions only.
+
+7.3 /proc/sys/net/can-j1939/transport_tx_retry_ms [int]
+
+ Controls how many time to wait before retrying to send an individual TP
+ flow or data packet after transmission failure (default 20).
+
+
+8. Credits
+--------------------------------
+
+ Kurt Van Dijck (j1939 core, transport protocol, API)
+ Pieter Beyens (j1939 core, address claiming)
+
diff --git a/MAINTAINERS b/MAINTAINERS
index b5266ad..ec7b016 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1648,6 +1648,14 @@ F: include/linux/can/error.h
F: include/linux/can/netlink.h
F: include/linux/can/platform/
+CAN-J1939 NETWORK LAYER
+M: Kurt Van Dijck <kurt.van.dijck-/BeEPy95v10@public.gmane.org>
+L: socketcan-core-0fE9KPoRgkgATYTw5x5z8w@public.gmane.org
+L: netdev-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
+S: Maintained
+F: net/can/j1939/
+F: include/linux/can/j1939.h
+
CELL BROADBAND ENGINE ARCHITECTURE
M: Arnd Bergmann <arnd-r2nGTMty4D4@public.gmane.org>
L: linuxppc-dev-uLR06cmDAlY/bJ5BZ2RsiQ@public.gmane.org
--
1.7.2.5
^ permalink raw reply related
* [PATCH v4 3/5] can-j1939: Import SAE J1939 stack
From: Kurt Van Dijck @ 2011-04-27 8:58 UTC (permalink / raw)
To: socketcan-core-0fE9KPoRgkgATYTw5x5z8w,
netdev-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20110427085330.GA757-ozGf4kBk5synFtIcQ8t7k3L8HoS0Hn3T@public.gmane.org>
Import SAE J1939
Signed-off-by: Kurt Van Dijck <kurt.van.dijck-/BeEPy95v10@public.gmane.org>
---
include/linux/can/Kbuild | 1 +
include/linux/can/j1939.h | 99 +++
net/can/Kconfig | 1 +
net/can/Makefile | 2 +
net/can/j1939/Kconfig | 22 +
net/can/j1939/Makefile | 14 +
net/can/j1939/address-claim.c | 227 +++++++
net/can/j1939/bus.c | 523 +++++++++++++++
net/can/j1939/filter.c | 76 +++
net/can/j1939/j1939-priv.h | 314 +++++++++
net/can/j1939/main.c | 458 +++++++++++++
net/can/j1939/proc.c | 104 +++
net/can/j1939/promisc.c | 43 ++
net/can/j1939/rtnl.c | 308 +++++++++
net/can/j1939/socket.c | 969 +++++++++++++++++++++++++++
net/can/j1939/transport.c | 1449 +++++++++++++++++++++++++++++++++++++++++
16 files changed, 4610 insertions(+), 0 deletions(-)
create mode 100644 include/linux/can/j1939.h
create mode 100644 net/can/j1939/Kconfig
create mode 100644 net/can/j1939/Makefile
create mode 100644 net/can/j1939/address-claim.c
create mode 100644 net/can/j1939/bus.c
create mode 100644 net/can/j1939/filter.c
create mode 100644 net/can/j1939/j1939-priv.h
create mode 100644 net/can/j1939/main.c
create mode 100644 net/can/j1939/proc.c
create mode 100644 net/can/j1939/promisc.c
create mode 100644 net/can/j1939/rtnl.c
create mode 100644 net/can/j1939/socket.c
create mode 100644 net/can/j1939/transport.c
diff --git a/include/linux/can/Kbuild b/include/linux/can/Kbuild
index 8cb05aa..0364eef 100644
--- a/include/linux/can/Kbuild
+++ b/include/linux/can/Kbuild
@@ -2,3 +2,4 @@ header-y += raw.h
header-y += bcm.h
header-y += error.h
header-y += netlink.h
+header-y += j1939.h
diff --git a/include/linux/can/j1939.h b/include/linux/can/j1939.h
new file mode 100644
index 0000000..7ff419e
--- /dev/null
+++ b/include/linux/can/j1939.h
@@ -0,0 +1,99 @@
+/*
+ * j1939.h
+ *
+ * Copyright (c) 2010-2011 EIA Electronics
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _J1939_H_
+#define _J1939_H_
+
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/can.h>
+
+#define J1939_IDLE_ADDR 0xfe
+#define J1939_NO_ADDR 0xff
+#define J1939_NO_NAME 0
+#define J1939_NO_PGN 0x40000
+/*
+ * J1939 Parameter Group Number
+ *
+ * bit 0-7 : PDU Specific (PS)
+ * bit 8-15 : PDU Format (PF)
+ * bit 16 : Data Page (DP)
+ * bit 17 : Reserved (R)
+ * bit 19-31 : set to zero
+ */
+typedef __u32 pgn_t;
+
+/*
+ * J1939 Priority
+ *
+ * bit 0-2 : Priority (P)
+ * bit 3-7 : set to zero
+ */
+typedef __u8 priority_t;
+
+/*
+ * J1939 NAME
+ *
+ * bit 0-20 : Identity Number
+ * bit 21-31 : Manufacturer Code
+ * bit 32-34 : ECU Instance
+ * bit 35-39 : Function Instance
+ * bit 40-47 : Function
+ * bit 48 : Reserved
+ * bit 49-55 : Vehicle System
+ * bit 56-59 : Vehicle System Instance
+ * bit 60-62 : Industry Group
+ * bit 63 : Arbitrary Address Capable
+ */
+typedef __u64 name_t;
+
+/*
+ * J1939 socket options
+ */
+#define SOL_CAN_J1939 (SOL_CAN_BASE + CAN_J1939)
+enum {
+ SO_J1939_FILTER = 1, /* set filters */
+ SO_J1939_PROMISC = 2, /* set/clr promiscuous mode */
+ SO_J1939_RECV_OWN = 3,
+ SO_J1939_SEND_PRIO = 4,
+};
+
+enum {
+ SCM_J1939_DEST_ADDR = 1,
+ SCM_J1939_DEST_NAME = 2,
+ SCM_J1939_PRIO = 3,
+};
+
+struct j1939_filter {
+ name_t name;
+ name_t name_mask;
+ __u8 addr;
+ __u8 addr_mask;
+ pgn_t pgn;
+ pgn_t pgn_mask;
+};
+
+/*
+ * RTNETLINK
+ */
+enum {
+ IFLA_J1939_UNSPEC,
+ IFLA_J1939_ENABLE,
+ IFLA_J1939_MAX,
+};
+
+enum {
+ IFA_J1939_UNSPEC,
+ IFA_J1939_ADDR,
+ IFA_J1939_NAME,
+ IFA_J1939_MAX,
+};
+
+#endif /* _J1939_H_ */
diff --git a/net/can/Kconfig b/net/can/Kconfig
index 89395b2..7feb58c 100644
--- a/net/can/Kconfig
+++ b/net/can/Kconfig
@@ -40,5 +40,6 @@ config CAN_BCM
CAN messages are used on the bus (e.g. in automotive environments).
To use the Broadcast Manager, use AF_CAN with protocol CAN_BCM.
+source "net/can/j1939/Kconfig"
source "drivers/net/can/Kconfig"
diff --git a/net/can/Makefile b/net/can/Makefile
index 2d3894b..953d851 100644
--- a/net/can/Makefile
+++ b/net/can/Makefile
@@ -10,3 +10,5 @@ can-raw-y := raw.o
obj-$(CONFIG_CAN_BCM) += can-bcm.o
can-bcm-y := bcm.o
+
+obj-$(CONFIG_CAN_J1939) += j1939/
diff --git a/net/can/j1939/Kconfig b/net/can/j1939/Kconfig
new file mode 100644
index 0000000..74d2a86
--- /dev/null
+++ b/net/can/j1939/Kconfig
@@ -0,0 +1,22 @@
+#
+# SAE J1939 network layer core configuration
+#
+
+config CAN_J1939
+ tristate "SAE J1939"
+ depends on CAN
+ ---help---
+ SAE J1939
+ Say Y to have in-kernel support for j1939 socket type. This
+ allows communication according to SAE j1939.
+ The relevant parts in kernel are
+ SAE j1939-21 (datalink & transport protocol)
+ & SAE j1939-81 (network management).
+
+config CAN_J1939_DEBUG
+ bool "debug SAE J1939"
+ depends on CAN_J1939
+ default n
+ ---help---
+ Say Y to add extra debug code (via printk) in the j1939 stack
+
diff --git a/net/can/j1939/Makefile b/net/can/j1939/Makefile
new file mode 100644
index 0000000..7ca2fc9
--- /dev/null
+++ b/net/can/j1939/Makefile
@@ -0,0 +1,14 @@
+
+obj-$(CONFIG_CAN_J1939) += can-j1939.o
+
+can-j1939-objs := main.o \
+ proc.o bus.o \
+ rtnl.o \
+ socket.o \
+ address-claim.o transport.o \
+ promisc.o filter.o
+
+ifeq ($(CONFIG_CAN_J1939_DEBUG),y)
+ EXTRA_CFLAGS += -DDEBUG
+endif
+
diff --git a/net/can/j1939/address-claim.c b/net/can/j1939/address-claim.c
new file mode 100644
index 0000000..826c7e9
--- /dev/null
+++ b/net/can/j1939/address-claim.c
@@ -0,0 +1,227 @@
+/*
+ * Copyright (c) 2010-2011 EIA Electronics
+ *
+ * Authors:
+ * Pieter Beyens <pieter.beyens-/BeEPy95v10@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the version 2 of the GNU General Public License
+ * as published by the Free Software Foundation
+ */
+
+/*
+ * J1939 Address Claiming.
+ * Address Claiming in the kernel
+ * - keeps track of the AC states of ECU's,
+ * - resolves NAME<=>SA taking into account the AC states of ECU's.
+ *
+ * All Address Claim msgs (including host-originated msg) are processed
+ * at the receive path (a sent msg is always received again via CAN echo).
+ * As such, the processing of AC msgs is done in the order on which msgs
+ * are sent on the bus.
+ *
+ * This module doesn't send msgs itself (e.g. replies on Address Claims),
+ * this is the responsibility of a user space application or daemon.
+ */
+
+#include <linux/skbuff.h>
+#include <linux/byteorder/generic.h>
+
+#include "j1939-priv.h"
+
+#define CANDATA2NAME(data) le64_to_cpup((uint64_t *)data)
+
+static inline int ac_msg_is_request_for_ac(struct sk_buff *skb)
+{
+ struct j1939_sk_buff_cb *sk_addr = (void *)skb->cb;
+ int req_pgn;
+
+ if ((skb->len < 3) || (sk_addr->pgn != PGN_REQUEST))
+ return 0;
+ req_pgn = skb->data[0] | (skb->data[1] << 8) | (skb->data[2] << 16);
+ return req_pgn = PGN_ADDRESS_CLAIMED;
+}
+
+static int j1939_verify_outgoing_address_claim(struct sk_buff *skb)
+{
+ struct j1939_sk_buff_cb *sk_addr = (void *)skb->cb;
+
+ if (skb->len != 8) {
+ j1939_notice("tx address claim with dlc %i\n", skb->len);
+ return -EPROTO;
+ }
+
+ if (sk_addr->src.name != CANDATA2NAME(skb->data)) {
+ j1939_notice("tx address claim with different name\n");
+ return -EPROTO;
+ }
+
+ if (sk_addr->src.addr == J1939_NO_ADDR) {
+ j1939_notice("tx address claim with broadcast sa\n");
+ return -EPROTO;
+ }
+
+ /* ac must always be a broadcast */
+ if (sk_addr->dst.name || (sk_addr->dst.addr != J1939_NO_ADDR)) {
+ j1939_notice("tx address claim with dest, not broadcast\n");
+ return -EPROTO;
+ }
+ return 0;
+}
+
+int j1939_send_address_claim(struct sk_buff *skb)
+{
+ int ret, sa;
+ struct j1939_sk_buff_cb *sk_addr = (void *)skb->cb;
+
+ /* network mgmt: address claiming msgs */
+ if (sk_addr->pgn == PGN_ADDRESS_CLAIMED) {
+ struct j1939_ecu *ecu;
+
+ ret = j1939_verify_outgoing_address_claim(skb);
+ /* return both when failure & when successfull */
+ if (ret < 0)
+ return ret;
+ ecu = j1939_ecu_find_by_name(sk_addr->src.name,
+ sk_addr->ifindex);
+ if (!ecu)
+ return -ENODEV;
+ if (!(ecu->flags & ECUFLAG_LOCAL)) {
+ put_j1939_ecu(ecu);
+ return -EREMOTE;
+ }
+
+ if (ecu->sa != sk_addr->src.addr)
+ /* hold further traffic for ecu, remove from parent */
+ j1939_ecu_remove_sa(ecu);
+ put_j1939_ecu(ecu);
+ } else if (sk_addr->src.name) {
+ /* assign source address */
+ sa = j1939_name_to_sa(sk_addr->src.name, sk_addr->ifindex);
+ if (!j1939_address_is_unicast(sa) &&
+ !ac_msg_is_request_for_ac(skb)) {
+ j1939_notice("tx drop: invalid sa for name "
+ "0x%016llx\n", sk_addr->src.name);
+ return -EADDRNOTAVAIL;
+ }
+ sk_addr->src.addr = sa;
+ }
+
+ /* assign destination address */
+ if (sk_addr->dst.name) {
+ sa = j1939_name_to_sa(sk_addr->dst.name, sk_addr->ifindex);
+ if (!j1939_address_is_unicast(sa)) {
+ j1939_notice("tx drop: invalid da for name "
+ "0x%016llx\n", sk_addr->dst.name);
+ return -EADDRNOTAVAIL;
+ }
+ sk_addr->dst.addr = sa;
+ }
+ return 0;
+}
+
+static struct j1939_ecu *j1939_process_address_claim(struct sk_buff *skb)
+{
+ struct j1939_sk_buff_cb *sk_addr = (void *)skb->cb;
+ struct j1939_ecu *ecu, *dut, **pref;
+ name_t name;
+
+ if (skb->len < 8) {
+ j1939_notice("rx address claim with wrong dlc %i\n", skb->len);
+ return ERR_PTR(-EPROTO);
+ }
+
+ name = CANDATA2NAME(skb->data);
+ if (!name) {
+ j1939_notice("rx address claim without name\n");
+ return ERR_PTR(-EPROTO);
+ }
+
+ if (!j1939_address_is_valid(sk_addr->src.addr)) {
+ j1939_notice("rx address claim with broadcast sa\n");
+ return ERR_PTR(-EPROTO);
+ }
+
+ ecu = j1939_ecu_get_register(name, sk_addr->ifindex, ECUFLAG_REMOTE, 1);
+ if (IS_ERR(ecu))
+ return ecu;
+ if ((ecu->flags & ECUFLAG_LOCAL) && !skb->sk)
+ j1939_warning("duplicate name on the bus %016llx!\n",
+ (long long)name);
+
+ if (sk_addr->src.addr >= J1939_IDLE_ADDR) {
+ j1939_ecu_remove_sa(ecu);
+ if (ecu->flags & ECUFLAG_REMOTE)
+ /* extra put => schedule removal */
+ j1939_ecu_unregister(ecu);
+ return ecu;
+ }
+
+ write_lock_bh(&ecu->parent->lock);
+ /* save new SA */
+ if (sk_addr->src.addr != ecu->sa)
+ j1939_ecu_remove_sa_locked(ecu);
+ ecu->sa = sk_addr->src.addr;
+ /* iterate this segment */
+ list_for_each_entry(dut, &ecu->parent->ecus, list) {
+ /* cancel pending claims for this SA */
+ /* this includes myself ! */
+ if (ecu->sa == dut->sa)
+ /*
+ * cancel pending claims for our new SA
+ * this includes 'ecu', since we will
+ * schedule a timer soon now
+ */
+ hrtimer_try_to_cancel(&dut->ac_timer);
+ if (dut->name > ecu->name)
+ dut->sa = J1939_IDLE_ADDR;
+ }
+
+ pref = &ecu->parent->ents[sk_addr->src.addr].ecu;
+ if (*pref && ((*pref)->name > ecu->name))
+ *pref = NULL;
+
+ /* schedule timer in 250 msec to commit address change */
+ hrtimer_start(&ecu->ac_timer, ktime_set(0, 250000000),
+ HRTIMER_MODE_REL);
+ write_unlock_bh(&ecu->parent->lock);
+
+ return ecu;
+}
+
+int j1939_recv_address_claim(struct sk_buff *skb)
+{
+ struct j1939_sk_buff_cb *sk_addr = (void *)skb->cb;
+ struct j1939_ecu *ecu;
+
+ /*
+ * network mgmt
+ */
+ if (sk_addr->pgn == PGN_ADDRESS_CLAIMED) {
+ ecu = j1939_process_address_claim(skb);
+ if (IS_ERR(ecu))
+ return PTR_ERR(ecu);
+ } else if (j1939_address_is_unicast(sk_addr->src.addr)) {
+ ecu = j1939_ecu_find_by_addr(sk_addr->src.addr,
+ sk_addr->ifindex);
+ } else {
+ ecu = NULL;
+ }
+
+ /* assign source stuff */
+ if (ecu) {
+ ecu->rxtime = ktime_get();
+ sk_addr->src.flags = ecu->flags;
+ sk_addr->src.name = ecu->name;
+ put_j1939_ecu(ecu);
+ }
+ /* assign destination stuff */
+ ecu = j1939_ecu_find_by_addr(sk_addr->dst.addr, sk_addr->ifindex);
+ if (ecu) {
+ sk_addr->dst.flags = ecu->flags;
+ sk_addr->dst.name = ecu->name;
+ put_j1939_ecu(ecu);
+ }
+ return 0;
+}
+
diff --git a/net/can/j1939/bus.c b/net/can/j1939/bus.c
new file mode 100644
index 0000000..c51245d
--- /dev/null
+++ b/net/can/j1939/bus.c
@@ -0,0 +1,523 @@
+/*
+ * Copyright (c) 2010-2011 EIA Electronics
+ *
+ * Authors:
+ * Kurt Van Dijck <kurt.van.dijck-/BeEPy95v10@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the version 2 of the GNU General Public License
+ * as published by the Free Software Foundation
+ */
+
+/*
+ * j1939-bus.c - bus for j1939 remote devices
+ * Since rtnetlink, no real bus is used.
+ */
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/workqueue.h>
+
+#include "j1939-priv.h"
+
+#define jseg_dbg(jseg, fmt, ...) \
+ pr_debug("j1939-%i: " fmt, (jseg)->ifindex, ##__VA_ARGS__)
+
+#define ecu_dbg(ecu, fmt, ...) \
+ pr_debug("j1939-%i,%016llx,%02x: " fmt, (ecu)->parent->ifindex, \
+ (ecu)->name, (ecu)->sa, ##__VA_ARGS__)
+#define ecu_alert(ecu, fmt, ...) \
+ pr_alert("j1939-%i,%016llx,%02x: " fmt, (ecu)->parent->ifindex, \
+ (ecu)->name, (ecu)->sa, ##__VA_ARGS__)
+
+static struct {
+ struct list_head list;
+ spinlock_t lock;
+} segments;
+
+struct j1939_segment *j1939_segment_find(int ifindex)
+{
+ struct j1939_segment *jseg;
+
+ spin_lock_bh(&segments.lock);
+ list_for_each_entry(jseg, &segments.list, flist) {
+ if (jseg->ifindex == ifindex) {
+ get_j1939_segment(jseg);
+ goto found;
+ }
+ }
+ jseg = NULL;
+found:
+ spin_unlock_bh(&segments.lock);
+ return jseg;
+}
+
+/*
+ * iterate over ECU's,
+ * and register flagged ecu's on their claimed SA
+ */
+static void j1939_segment_ac_task(unsigned long val)
+{
+ struct j1939_segment *jseg = (void *)val;
+ struct j1939_ecu *ecu;
+
+ write_lock_bh(&jseg->lock);
+ list_for_each_entry(ecu, &jseg->ecus, list) {
+ /* next 2 (read & set) could be merged into xxx? */
+ if (!atomic_read(&ecu->ac_delay_expired))
+ continue;
+ atomic_set(&ecu->ac_delay_expired, 0);
+ if (j1939_address_is_unicast(ecu->sa))
+ ecu->parent->ents[ecu->sa].ecu = ecu;
+ }
+ write_unlock_bh(&jseg->lock);
+}
+/*
+ * segment device interface
+ */
+static void cb_put_j1939_segment(struct kref *kref)
+{
+ struct j1939_segment *jseg =
+ container_of(kref, struct j1939_segment, kref);
+
+ tasklet_disable_nosync(&jseg->ac_task);
+ kfree(jseg);
+}
+
+void put_j1939_segment(struct j1939_segment *segment)
+{
+ kref_put(&segment->kref, cb_put_j1939_segment);
+}
+
+int j1939_segment_register(struct net_device *netdev)
+{
+ int ret;
+ struct j1939_segment *jseg;
+
+ jseg = j1939_segment_find(netdev->ifindex);
+ if (jseg) {
+ put_j1939_segment(jseg);
+ ret = -EALREADY;
+ goto fail_exist;
+ }
+ jseg = kzalloc(sizeof(*jseg), GFP_KERNEL);
+ if (!jseg) {
+ ret = -ENOMEM;
+ goto fail_malloc;
+ }
+ tasklet_init(&jseg->ac_task, j1939_segment_ac_task,
+ (unsigned long)jseg);
+ rwlock_init(&jseg->lock);
+ INIT_LIST_HEAD(&jseg->ecus);
+ INIT_LIST_HEAD(&jseg->flist);
+ jseg->ifindex = netdev->ifindex;
+
+ kref_init(&jseg->kref);
+
+ spin_lock_bh(&segments.lock);
+ list_add_tail(&jseg->flist, &segments.list);
+ spin_unlock_bh(&segments.lock);
+
+ jseg_dbg(jseg, "register\n");
+ return 0;
+
+fail_malloc:
+fail_exist:
+ return ret;
+}
+
+void j1939_segment_unregister(struct j1939_segment *jseg)
+{
+ struct j1939_ecu *ecu;
+
+ if (!jseg)
+ return;
+
+ spin_lock_bh(&segments.lock);
+ list_del_init(&jseg->flist);
+ spin_unlock_bh(&segments.lock);
+
+ write_lock_bh(&jseg->lock);
+ while (!list_empty(&jseg->ecus)) {
+ ecu = list_first_entry(&jseg->ecus, struct j1939_ecu, list);
+ write_unlock_bh(&jseg->lock);
+ j1939_ecu_unregister(ecu);
+ write_lock_bh(&jseg->lock);
+ }
+ write_unlock_bh(&jseg->lock);
+ jseg_dbg(jseg, "unregister\n");
+ put_j1939_segment(jseg);
+}
+
+/*
+ * ECU device interface
+ */
+static enum hrtimer_restart j1939_ecu_timer_handler(struct hrtimer *hrtimer)
+{
+ struct j1939_ecu *ecu =
+ container_of(hrtimer, struct j1939_ecu, ac_timer);
+
+ atomic_set(&ecu->ac_delay_expired, 1);
+ tasklet_schedule(&ecu->parent->ac_task);
+ return HRTIMER_NORESTART;
+}
+
+static void cb_put_j1939_ecu(struct kref *kref)
+{
+ struct j1939_ecu *ecu =container_of(kref, struct j1939_ecu, kref);
+
+ kfree(ecu);
+}
+void put_j1939_ecu(struct j1939_ecu *ecu)
+{
+ kref_put(&ecu->kref, cb_put_j1939_ecu);
+}
+
+struct j1939_ecu *j1939_ecu_get_register(name_t name, int ifindex, int flags,
+ int return_existing)
+{
+ struct j1939_segment *parent;
+ struct j1939_ecu *ecu, *dut;
+
+ if (!ifindex || !name) {
+ pr_alert("%s(%i, %016llx) invalid\n",
+ __func__, ifindex, (long long)name);
+ return ERR_PTR(-EINVAL);
+ }
+
+ parent = j1939_segment_find(ifindex);
+ if (!parent) {
+ pr_alert("%s %i: segment not found\n", __func__, ifindex);
+ return ERR_PTR(-EINVAL);
+ }
+ if (return_existing) {
+ read_lock_bh(&parent->lock);
+ /* test for existing name */
+ list_for_each_entry(dut, &parent->ecus, list) {
+ if (dut->name == name) {
+ get_j1939_ecu(dut);
+ read_unlock_bh(&parent->lock);
+ return dut;
+ }
+ }
+ read_unlock_bh(&parent->lock);
+ }
+ /* alloc */
+ ecu = kzalloc(sizeof(*ecu), gfp_any());
+ if (!ecu)
+ /* should we look for an existing ecu */
+ return ERR_PTR(-ENOMEM);
+ kref_init(&ecu->kref);
+ ecu->sa = J1939_IDLE_ADDR;
+ ecu->name = name;
+ ecu->flags = flags;
+
+ hrtimer_init(&ecu->ac_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ ecu->ac_timer.function = j1939_ecu_timer_handler;
+ INIT_LIST_HEAD(&ecu->list);
+
+ /* first add to internal list */
+ write_lock_bh(&parent->lock);
+ /* test for duplicate name */
+ list_for_each_entry(dut, &parent->ecus, list) {
+ if (dut->name == ecu->name)
+ goto duplicate;
+ }
+ get_j1939_ecu(ecu);
+ /* a ref to parent is held */
+ ecu->parent = parent;
+ list_add_tail(&ecu->list, &parent->ecus);
+ write_unlock_bh(&parent->lock);
+ ecu_dbg(ecu, "register\n");
+ return ecu;
+
+duplicate:
+ get_j1939_ecu(dut);
+ write_unlock_bh(&parent->lock);
+ put_j1939_segment(parent);
+ if (return_existing)
+ return dut;
+ ecu_alert(ecu, "duplicate name\n");
+ put_j1939_ecu(ecu);
+ return ERR_PTR(-EEXIST);
+}
+
+void j1939_ecu_unregister(struct j1939_ecu *ecu)
+{
+ BUG_ON(!ecu);
+ ecu_dbg(ecu, "unregister\n");
+ hrtimer_try_to_cancel(&ecu->ac_timer);
+
+ write_lock_bh(&ecu->parent->lock);
+ j1939_ecu_remove_sa_locked(ecu);
+ list_del_init(&ecu->list);
+ write_unlock_bh(&ecu->parent->lock);
+ /* put segment, reverting the effect done by ..._register() */
+ put_j1939_segment(ecu->parent);
+ put_j1939_ecu(ecu);
+}
+
+struct j1939_ecu *j1939_ecu_find_by_addr(int sa, int ifindex)
+{
+ struct j1939_ecu *ecu;
+ struct j1939_segment *parent;
+
+ if (!j1939_address_is_unicast(sa))
+ return NULL;
+ parent = j1939_segment_find(ifindex);
+ if (!parent)
+ return NULL;
+ read_lock_bh(&parent->lock);
+ ecu = parent->ents[sa].ecu;
+ if (ecu)
+ get_j1939_ecu(ecu);
+ read_unlock_bh(&parent->lock);
+ put_j1939_segment(parent);
+ return ecu;
+}
+
+int j1939_name_to_sa(uint64_t name, int ifindex)
+{
+ struct j1939_ecu *ecu;
+ struct j1939_segment *parent;
+ int sa;
+
+ if (!name)
+ return J1939_IDLE_ADDR;
+ parent = j1939_segment_find(ifindex);
+ if (!parent)
+ return J1939_IDLE_ADDR;
+
+ sa = J1939_IDLE_ADDR;
+ read_lock_bh(&parent->lock);
+ list_for_each_entry(ecu, &parent->ecus, list) {
+ if (ecu->name == name) {
+ if ((sa == J1939_IDLE_ADDR) &&
+ (parent->ents[ecu->sa].ecu == ecu))
+ /* ecu's SA is registered */
+ sa = ecu->sa;
+ break;
+ }
+ }
+ read_unlock_bh(&parent->lock);
+ put_j1939_segment(parent);
+ return sa;
+}
+
+struct j1939_ecu *j1939_ecu_find_segment_default_tx(int ifindex,
+ name_t *name, uint8_t *addr)
+{
+ struct j1939_ecu *ecu;
+ struct j1939_segment *parent;
+ struct addr_ent *paddr;
+ int j;
+
+ if (ifindex <= 0)
+ return ERR_PTR(-EINVAL);
+ parent = j1939_segment_find(ifindex);
+ if (!parent)
+ return ERR_PTR(-ENETUNREACH);
+ read_lock_bh(&parent->lock);
+ list_for_each_entry(ecu, &parent->ecus, list) {
+ if (ecu->flags & ECUFLAG_LOCAL) {
+ get_j1939_ecu(ecu);
+ if (name)
+ *name = ecu->name;
+ if (addr)
+ *addr = ecu->sa;
+ goto found;
+ }
+ }
+ ecu = NULL;
+ for (j = 0, paddr = parent->ents; j < J1939_IDLE_ADDR; ++j, ++paddr) {
+ if (paddr->ecu)
+ continue;
+ if (paddr->flags & ECUFLAG_LOCAL) {
+ if (name)
+ *name = 0;
+ if (addr)
+ *addr = j;
+ goto found;
+ }
+ }
+ ecu = ERR_PTR(-EHOSTDOWN);
+found:
+ read_unlock_bh(&parent->lock);
+ put_j1939_segment(parent);
+ return ecu;
+}
+
+/* ecu lookup helper */
+static struct j1939_ecu *_j1939_ecu_find_by_name(name_t name,
+ struct j1939_segment *jseg)
+{
+ struct j1939_ecu *ecu;
+
+ read_lock_bh(&jseg->lock);
+ list_for_each_entry(ecu, &jseg->ecus, list) {
+ if (ecu->name == name) {
+ get_j1939_ecu(ecu);
+ goto found_on_intf;
+ }
+ }
+ ecu = NULL;
+found_on_intf:
+ read_unlock_bh(&jseg->lock);
+ return ecu;
+}
+
+/* ecu lookup by name */
+struct j1939_ecu *j1939_ecu_find_by_name(name_t name, int ifindex)
+{
+ struct j1939_ecu *ecu;
+ struct j1939_segment *jseg;
+
+ if (!name)
+ return NULL;
+ if (ifindex) {
+ jseg = j1939_segment_find(ifindex);
+ if (!jseg)
+ return NULL;
+ ecu = _j1939_ecu_find_by_name(name, jseg);
+ put_j1939_segment(jseg);
+ return ecu;
+ }
+ /* iterate segments */
+ spin_lock_bh(&segments.lock);
+ list_for_each_entry(jseg, &segments.list, flist) {
+ get_j1939_segment(jseg);
+ ecu = _j1939_ecu_find_by_name(name, jseg);
+ put_j1939_segment(jseg);
+ if (ecu)
+ goto found;
+ }
+ ecu = NULL;
+found:
+ spin_unlock_bh(&segments.lock);
+ return ecu;
+}
+
+/* PROC */
+static int j1939_proc_addr(struct seq_file *sqf, void *v)
+{
+ struct j1939_segment *jseg;
+ struct net_device *netdev;
+ struct addr_ent *paddr;
+ int j, flags;
+ ktime_t now;
+ struct timeval tv;
+
+ now = ktime_get();
+ seq_printf(sqf, "iface\tSA\tflags\trxtime\n");
+ spin_lock_bh(&segments.lock);
+ list_for_each_entry(jseg, &segments.list, flist) {
+ get_j1939_segment(jseg);
+ netdev = dev_get_by_index(&init_net, jseg->ifindex);
+ if (!netdev) {
+ pr_alert("j1939 proc: ifindex %i not found\n",
+ jseg->ifindex);
+ put_j1939_segment(jseg);
+ continue;
+ }
+ read_lock_bh(&jseg->lock);
+ for (j = 0, paddr = jseg->ents; j < J1939_IDLE_ADDR;
+ ++j, ++paddr) {
+ flags = paddr->flags;
+ if (paddr->ecu)
+ flags |= paddr->ecu->flags;
+ tv = ktime_to_timeval(ktime_sub(now, paddr->rxtime));
+ if (!paddr->flags && !paddr->ecu)
+ continue;
+ seq_printf(sqf, "%s\t%02x\t%c%c%c%c\t-%lu.%06lu\n",
+ netdev->name, j,
+ (flags & ECUFLAG_LOCAL) ? 'L' : '-',
+ (flags & ECUFLAG_REMOTE) ? 'R' : '-',
+ (paddr->flags) ? 'S' : '-',
+ paddr->ecu ? 'E' : '-',
+ tv.tv_sec, tv.tv_usec);
+ }
+ read_unlock_bh(&jseg->lock);
+ dev_put(netdev);
+ put_j1939_segment(jseg);
+ }
+ spin_unlock_bh(&segments.lock);
+ return 0;
+}
+
+static int j1939_proc_ecu(struct seq_file *sqf, void *v)
+{
+ struct j1939_segment *jseg;
+ struct j1939_ecu *ecu;
+ struct net_device *netdev;
+ ktime_t now;
+ struct timeval tv;
+ char sa[4];
+
+ now = ktime_get();
+ seq_printf(sqf, "iface\taddr\tname\tflags\trxtime\n");
+ spin_lock_bh(&segments.lock);
+ list_for_each_entry(jseg, &segments.list, flist) {
+ get_j1939_segment(jseg);
+ netdev = dev_get_by_index(&init_net, jseg->ifindex);
+ if (!netdev) {
+ pr_alert("j1939 proc: ifindex %i not found\n",
+ jseg->ifindex);
+ put_j1939_segment(jseg);
+ continue;
+ }
+ read_lock_bh(&jseg->lock);
+ list_for_each_entry(ecu, &jseg->ecus, list) {
+ tv = ktime_to_timeval(ktime_sub(now, ecu->rxtime));
+ if (j1939_address_is_unicast(ecu->sa) &&
+ (ecu->parent->ents[ecu->sa].ecu == ecu))
+ snprintf(sa, sizeof(sa), "%02x", ecu->sa);
+ else
+ strcpy(sa, "-");
+ seq_printf(sqf, "%s\t%s\t%016llx\t%c\t-%lu.%06lu\n",
+ netdev->name, sa,
+ (unsigned long long)ecu->name,
+ (ecu->flags & ECUFLAG_LOCAL) ? 'L' : 'R',
+ tv.tv_sec, tv.tv_usec);
+ }
+ read_unlock_bh(&jseg->lock);
+ dev_put(netdev);
+ put_j1939_segment(jseg);
+ }
+ spin_unlock_bh(&segments.lock);
+ return 0;
+}
+
+/* exported init */
+int __init j1939bus_module_init(void)
+{
+ INIT_LIST_HEAD(&segments.list);
+ spin_lock_init(&segments.lock);
+ j1939_proc_add("addr", j1939_proc_addr, NULL);
+ j1939_proc_add("ecu", j1939_proc_ecu, NULL);
+ return 0;
+}
+
+void j1939bus_module_exit(void)
+{
+ struct j1939_segment *jseg;
+ struct net_device *netdev;
+
+ spin_lock_bh(&segments.lock);
+ while (!list_empty(&segments.list)) {
+ jseg = list_first_entry(&segments.list,
+ struct j1939_segment, flist);
+ netdev = dev_get_by_index(&init_net, jseg->ifindex);
+ spin_unlock_bh(&segments.lock);
+ j1939_segment_detach(netdev);
+ dev_put(netdev);
+ spin_lock_bh(&segments.lock);
+ }
+ spin_unlock_bh(&segments.lock);
+
+ j1939_proc_remove("ecu");
+ j1939_proc_remove("addr");
+}
+
+
diff --git a/net/can/j1939/filter.c b/net/can/j1939/filter.c
new file mode 100644
index 0000000..c10f7b9
--- /dev/null
+++ b/net/can/j1939/filter.c
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2010-2011 EIA Electronics
+ *
+ * Authors:
+ * Pieter Beyens <pieter.beyens-/BeEPy95v10@public.gmane.org>
+ * Kurt Van Dijck <kurt.van.dijck-/BeEPy95v10@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the version 2 of the GNU General Public License
+ * as published by the Free Software Foundation
+ */
+
+#include <linux/list.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/skbuff.h>
+#include <linux/spinlock.h>
+
+#include "j1939-priv.h"
+
+static LIST_HEAD(filters);
+DEFINE_RWLOCK(j1939_receiver_rwlock); /* protects the filter list */
+
+struct filter {
+ struct list_head list;
+ void *vp;
+ void (*fn)(struct sk_buff *, void *);
+};
+
+int j1939_recv_distribute(struct sk_buff *skb)
+{
+ struct filter *filter;
+
+ read_lock_bh(&j1939_receiver_rwlock);
+ list_for_each_entry(filter, &filters, list)
+ filter->fn(skb, filter->vp);
+ read_unlock_bh(&j1939_receiver_rwlock);
+
+ return 0;
+}
+
+int j1939_recv_add(void *vp, void (*fn)(struct sk_buff *, void *))
+{
+ struct filter *f;
+
+ f = kzalloc(sizeof(*f), GFP_KERNEL);
+ if (!f)
+ return -ENOMEM;
+
+ f->vp = vp;
+ f->fn = fn;
+
+ j1939_recv_suspend();
+ list_add(&f->list, &filters);
+ j1939_recv_resume();
+ return 0;
+}
+
+int j1939_recv_remove(void *vp, void (*fn)(struct sk_buff *, void *))
+{
+ struct filter *filter;
+ int found = 0;
+
+ j1939_recv_suspend();
+ list_for_each_entry(filter, &filters, list) {
+ if ((filter->vp == vp) && (filter->fn == fn)) {
+ list_del_init(&filter->list);
+ kfree(filter);
+ found = 1;
+ break;
+ }
+ }
+ j1939_recv_resume();
+ return found ? 0 : -ENOENT;
+}
+
diff --git a/net/can/j1939/j1939-priv.h b/net/can/j1939/j1939-priv.h
new file mode 100644
index 0000000..3531ee5
--- /dev/null
+++ b/net/can/j1939/j1939-priv.h
@@ -0,0 +1,314 @@
+/*
+ * j1939-priv.h
+ *
+ * Copyright (c) 2010-2011 EIA Electronics
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _J1939_PRIV_H_
+#define _J1939_PRIV_H_
+
+#include <linux/kref.h>
+#include <linux/list.h>
+#include <net/sock.h>
+
+#include <linux/seq_file.h>
+#include <linux/proc_fs.h>
+#include <linux/module.h>
+#include <linux/can/j1939.h>
+#include <linux/atomic.h>
+
+/* TODO: return ENETRESET on busoff. */
+
+#define ECUFLAG_LOCAL 0x01
+#define ECUFLAG_REMOTE 0x02
+
+#define PGN_REQUEST 0x0ea00
+#define PGN_ADDRESS_CLAIMED 0x0ee00
+#define PGN_MAX 0x3ffff
+
+#define SA_MAX_UNICAST 0xfd
+/*
+ * j1939 devices
+ */
+struct j1939_ecu {
+ struct list_head list;
+ ktime_t rxtime;
+ name_t name;
+ int flags;
+ uint8_t sa;
+ /*
+ * atomic flag, set by ac_timer
+ * cleared/processed by segment's tasklet
+ * indicates that this ecu successfully claimed @sa as its address
+ * By communicating this from the ac_timer event to segments tasklet,
+ * a context locking problem is solved. All other 'ecu readers'
+ * must only lock with _bh, not with _irq.
+ */
+ atomic_t ac_delay_expired;
+ struct hrtimer ac_timer;
+ struct kref kref;
+ struct j1939_segment *parent;
+};
+#define to_j1939_ecu(x) container_of((x), struct j1939_ecu, dev)
+
+struct j1939_segment {
+ struct list_head ecus; /*
+ * local list entry in parent
+ * These allow irq (& softirq) context lookups on j1939 devices
+ * This approach (seperate lists) is done as the other 2 alternatives
+ * are not easier or even wrong
+ * 1) using the pure kobject methods involves mutexes, which are not
+ * allowed in irq context.
+ * 2) duplicating data structures would require a lot of synchronization
+ * code
+ * usage:
+ */
+ rwlock_t lock; /*
+ * segments need a lock to protect the above list
+ */
+ struct list_head flist; /*
+ * list entry for use by interrupt lookup routines
+ */
+ int ifindex;
+ struct addr_ent {
+ ktime_t rxtime;
+ struct j1939_ecu *ecu;
+ int flags;
+ } ents[256];
+
+ /*
+ * tasklet to process ecu address claimed events.
+ * These events raise in hardirq context. Signalling the event
+ * and scheduling this tasklet successfully moves the
+ * event to softirq context
+ */
+ struct tasklet_struct ac_task;
+ /*
+ * list of 256 ecu ptrs, that cache the claimed addresses.
+ * also protected by the above lock
+ * don't use directly, use j1939_ecu_set_address() instead
+ */
+ struct kref kref;
+};
+#define to_j1939_segment(x) container_of((x), struct j1939_segment, dev)
+
+extern void put_j1939_ecu(struct j1939_ecu *ecu);
+extern void put_j1939_segment(struct j1939_segment *segment);
+static inline struct j1939_ecu *get_j1939_ecu(struct j1939_ecu *dut)
+{
+ kref_get(&dut->kref);
+ return dut;
+}
+static inline struct j1939_segment *get_j1939_segment(struct j1939_segment *dut)
+{
+ kref_get(&dut->kref);
+ return dut;
+}
+
+/*
+ * conversion function between (struct sock | struct sk_buff)->sk_priority
+ * from linux and j1939 priority field
+ */
+static inline int j1939_prio(int sk_priority)
+{
+ if (sk_priority < 0)
+ return 6; /* default */
+ else if (sk_priority > 7)
+ return 0;
+ else
+ return 7 - sk_priority;
+}
+static inline int j1939_to_sk_priority(int j1939_prio)
+{
+ return 7 - j1939_prio;
+}
+
+static inline int j1939_address_is_valid(uint8_t sa)
+{
+ return sa != J1939_NO_ADDR;
+}
+
+static inline int j1939_address_is_unicast(uint8_t sa)
+{
+ return sa <= SA_MAX_UNICAST;
+}
+
+static inline int pgn_is_pdu1(pgn_t pgn)
+{
+ /* ignore dp & res bits for this */
+ return (pgn & 0xff00) < 0xf000;
+}
+
+static inline int pgn_is_valid(pgn_t pgn)
+{
+ return pgn <= PGN_MAX;
+}
+
+/* utility to correctly unregister a SA */
+static inline void j1939_ecu_remove_sa_locked(struct j1939_ecu *ecu)
+{
+ if (!j1939_address_is_unicast(ecu->sa))
+ return;
+ if (ecu->parent->ents[ecu->sa].ecu == ecu)
+ ecu->parent->ents[ecu->sa].ecu = NULL;
+}
+
+static inline void j1939_ecu_remove_sa(struct j1939_ecu *ecu)
+{
+ if (!j1939_address_is_unicast(ecu->sa))
+ return;
+ write_lock_bh(&ecu->parent->lock);
+ j1939_ecu_remove_sa_locked(ecu);
+ write_unlock_bh(&ecu->parent->lock);
+}
+
+extern int j1939_name_to_sa(uint64_t name, int ifindex);
+extern struct j1939_ecu *j1939_ecu_find_by_addr(int sa, int ifindex);
+extern struct j1939_ecu *j1939_ecu_find_by_name(name_t name, int ifindex);
+/* find_by_name, with kref & read_lock taken */
+extern struct j1939_ecu *j1939_ecu_find_segment_default_tx(
+ int ifindex, name_t *pname, uint8_t *paddr);
+
+extern void j1939_put_promisc_receiver(int ifindex);
+extern void j1939_get_promisc_receiver(int ifindex);
+
+extern int j1939_proc_add(const char *file,
+ int (*seq_show)(struct seq_file *sqf, void *v),
+ write_proc_t write);
+extern void j1939_proc_remove(const char *file);
+
+extern const char j1939_procname[];
+/* j1939 printk */
+#define j1939_printk(level, ...) printk(level "J1939 " __VA_ARGS__)
+
+#define j1939_err(...) j1939_printk(KERN_ERR , __VA_ARGS__)
+#define j1939_warning(...) j1939_printk(KERN_WARNING , __VA_ARGS__)
+#define j1939_notice(...) j1939_printk(KERN_NOTICE , __VA_ARGS__)
+#define j1939_info(...) j1939_printk(KERN_INFO , __VA_ARGS__)
+#ifdef DEBUG
+#define j1939_debug(...) j1939_printk(KERN_DEBUG , __VA_ARGS__)
+#else
+#define j1939_debug(...)
+#endif
+
+struct sk_buff;
+
+/* control buffer of the sk_buff */
+struct j1939_sk_buff_cb {
+ int ifindex;
+ priority_t priority;
+ struct {
+ name_t name;
+ uint8_t addr;
+ int flags;
+ } src, dst;
+ pgn_t pgn;
+ int msg_flags;
+ /* for tx, MSG_SYN will be used to sync on sockets */
+};
+#define J1939_MSG_RESERVED MSG_SYN
+#define J1939_MSG_SYNC MSG_SYN
+
+static inline int j1939cb_is_broadcast(const struct j1939_sk_buff_cb *cb)
+{
+ return (!cb->dst.name && (cb->dst.addr >= 0xff));
+}
+
+/* J1939 stack */
+enum {
+ j1939_level_can,
+ j1939_level_transport,
+ j1939_level_sky,
+};
+
+#define RESULT_STOP 1
+/*
+ * return RESULT_STOP when stack processing may stop.
+ * it is up to the stack entry itself to kfree_skb() the sk_buff
+ */
+
+extern int j1939_send(struct sk_buff *, int level);
+extern int j1939_recv(struct sk_buff *, int level);
+
+/* stack entries */
+extern int j1939_recv_promisc(struct sk_buff *);
+extern int j1939_send_transport(struct sk_buff *);
+extern int j1939_recv_transport(struct sk_buff *);
+extern int j1939_send_address_claim(struct sk_buff *);
+extern int j1939_recv_address_claim(struct sk_buff *);
+
+extern int j1939_recv_distribute(struct sk_buff *);
+
+/* network management */
+/*
+ * j1939_ecu_get_register
+ * 'create' & 'register' & 'get' new ecu
+ * when a matching ecu already exists, the behaviour depends
+ * on @return_existing.
+ * when @return_existing is 0, -EEXISTS is returned
+ * when @return_exsiting is 1, that ecu is 'get' & returned.
+ * @flags is only used when creating new ecu.
+ */
+extern struct j1939_ecu *j1939_ecu_get_register(name_t name, int ifindex,
+ int flags, int return_existing);
+extern void j1939_ecu_unregister(struct j1939_ecu *);
+
+extern int j1939_segment_attach(struct net_device *);
+extern int j1939_segment_detach(struct net_device *);
+
+extern int j1939_segment_register(struct net_device *);
+extern void j1939_segment_unregister(struct j1939_segment *);
+extern struct j1939_segment *j1939_segment_find(int ifindex);
+
+extern void j1939sk_netdev_event(int ifindex, int error_code);
+
+/* add/remove receiver */
+extern int j1939_recv_add(void *vp, void (*fn)(struct sk_buff *, void *));
+extern int j1939_recv_remove(void *vp, void (*fn)(struct sk_buff *, void *));
+
+/*
+ * provide public access to this lock
+ * so sparse can verify the context balance
+ */
+extern rwlock_t j1939_receiver_rwlock;
+static inline void j1939_recv_suspend(void)
+{
+ write_lock_bh(&j1939_receiver_rwlock);
+}
+
+static inline void j1939_recv_resume(void)
+{
+ write_unlock_bh(&j1939_receiver_rwlock);
+}
+
+/* locks the recv module */
+extern void j1939_recv_suspend(void);
+extern void j1939_recv_resume(void);
+
+/*
+ * decrement pending skb for a j1939 socket
+ */
+extern void j1939_sock_pending_del(struct sock *sk);
+
+/* seperate module-init/modules-exit's */
+extern __init int j1939_proc_module_init(void);
+extern __init int j1939bus_module_init(void);
+extern __init int j1939sk_module_init(void);
+extern __init int j1939tp_module_init(void);
+
+extern void j1939_proc_module_exit(void);
+extern void j1939bus_module_exit(void);
+extern void j1939sk_module_exit(void);
+extern void j1939tp_module_exit(void);
+
+/* rtnetlink */
+extern const struct rtnl_af_ops j1939_rtnl_af_ops;
+extern int j1939rtnl_new_addr(struct sk_buff *, struct nlmsghdr *, void *arg);
+extern int j1939rtnl_del_addr(struct sk_buff *, struct nlmsghdr *, void *arg);
+extern int j1939rtnl_dump_addr(struct sk_buff *, struct netlink_callback *);
+
+#endif /* _J1939_PRIV_H_ */
diff --git a/net/can/j1939/main.c b/net/can/j1939/main.c
new file mode 100644
index 0000000..7edf843
--- /dev/null
+++ b/net/can/j1939/main.c
@@ -0,0 +1,458 @@
+/*
+ * Copyright (c) 2010-2011 EIA Electronics
+ *
+ * Authors:
+ * Kurt Van Dijck <kurt.van.dijck-/BeEPy95v10@public.gmane.org>
+ * Pieter Beyens <pieter.beyens-/BeEPy95v10@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the version 2 of the GNU General Public License
+ * as published by the Free Software Foundation
+ */
+
+/*
+ * Core of can-j1939 that links j1939 to CAN.
+ */
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/socket.h>
+#include <linux/list.h>
+#include <linux/if_arp.h>
+#include <net/tcp_states.h>
+
+#include <linux/can.h>
+#include <linux/can/core.h>
+#include "j1939-priv.h"
+
+MODULE_DESCRIPTION("PF_CAN SAE J1939");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("EIA Electronics (Kurt Van Dijck & Pieter Beyens)");
+
+static struct {
+ struct notifier_block notifier;
+} s;
+
+/* LOWLEVEL CAN interface */
+
+/* CAN_HDR: #bytes before can_frame data part */
+#define CAN_HDR (offsetof(struct can_frame, data))
+/* CAN_FTR: #bytes beyond data part */
+#define CAN_FTR (sizeof(struct can_frame)-CAN_HDR-\
+ sizeof(((struct can_frame *)0)->data))
+
+static void j1939_recv_ecu_flags(struct sk_buff *skb, void *data)
+{
+ struct j1939_segment *jseg = data;
+ struct j1939_sk_buff_cb *cb = (void *)skb->cb;
+ struct addr_ent *paddr;
+
+ if (!jseg)
+ return;
+ write_lock_bh(&jseg->lock);
+ if (j1939_address_is_unicast(cb->src.addr)) {
+ paddr = &jseg->ents[cb->src.addr];
+ paddr->rxtime = ktime_get();
+ if (0x0ee00 == cb->pgn) {
+ /* do not touch many things for Address claims */
+ } else if (paddr->ecu) {
+ paddr->ecu->rxtime = paddr->rxtime;
+ cb->src.flags = paddr->ecu->flags;
+ } else {
+ if (!paddr->flags)
+ paddr->flags |= ECUFLAG_REMOTE;
+ cb->src.flags = paddr->flags;
+ }
+ }
+
+ if (j1939_address_is_unicast(cb->dst.addr)) {
+ paddr = &jseg->ents[cb->dst.addr];
+ if (paddr->ecu)
+ cb->dst.flags = paddr->ecu->flags;
+ else
+ cb->dst.flags = paddr->flags ?: ECUFLAG_REMOTE;
+ }
+ write_unlock_bh(&jseg->lock);
+}
+
+/* lowest layer */
+static void j1939_can_recv(struct sk_buff *skb, void *data)
+{
+ int orig_len;
+ struct j1939_sk_buff_cb *sk_addr;
+ struct can_frame *msg;
+ uint8_t saved_cb[sizeof(skb->cb)];
+
+ BUILD_BUG_ON(sizeof(*sk_addr) > sizeof(skb->cb));
+ /*
+ * get a pointer to the header of the skb
+ * the skb payload (pointer) is moved, so that the next skb_data
+ * returns the actual payload
+ */
+ msg = (void *)skb->data;
+ orig_len = skb->len;
+ skb_pull(skb, CAN_HDR);
+ /* fix length, set to dlc, with 8 maximum */
+ skb_trim(skb, min_t(uint8_t, msg->can_dlc, 8));
+
+ /* set addr */
+ sk_addr = (struct j1939_sk_buff_cb *)skb->cb;
+ memcpy(saved_cb, sk_addr, sizeof(saved_cb));
+ memset(sk_addr, 0, sizeof(*sk_addr));
+ if (skb->dev)
+ sk_addr->ifindex = skb->dev->ifindex;
+ sk_addr->priority = (msg->can_id & 0x1c000000) >> 26;
+ sk_addr->src.addr = msg->can_id & 0xff;
+ sk_addr->pgn = (msg->can_id & 0x3ffff00) >> 8;
+ if (pgn_is_pdu1(sk_addr->pgn)) {
+ /* Type 1: with destination address */
+ sk_addr->dst.addr = sk_addr->pgn & 0xff;
+ /* normalize pgn: strip dst address */
+ sk_addr->pgn &= 0x3ff00;
+ } else {
+ /* set broadcast address */
+ sk_addr->dst.addr = J1939_NO_ADDR;
+ }
+ j1939_recv_ecu_flags(skb, data);
+ j1939_recv(skb, j1939_level_can);
+
+ /* restore the original skb, should always work */
+ skb_push(skb, CAN_HDR);
+ /* no safety check, it just restores the skbuf's contents */
+ __skb_trim(skb, orig_len);
+ memcpy(sk_addr, saved_cb, sizeof(saved_cb));
+}
+
+static int j1939_send_can(struct sk_buff *skb)
+{
+ int ret, dlc;
+ canid_t canid;
+ struct j1939_sk_buff_cb *sk_addr;
+ struct net_device *netdev = NULL;
+ struct can_frame *msg;
+
+ dlc = skb->len;
+ if (dlc > 8)
+ return -EMSGSIZE;
+ ret = pskb_expand_head(skb, SKB_DATA_ALIGN(CAN_HDR),
+ CAN_FTR + (8-dlc), GFP_ATOMIC);
+ if (ret < 0)
+ return ret;
+
+ msg = (void *)skb_push(skb, CAN_HDR);
+ BUG_ON(!msg);
+ /* make it a full can frame */
+ skb_put(skb, CAN_FTR + (8 - dlc));
+
+ sk_addr = (struct j1939_sk_buff_cb *)skb->cb;
+ canid = CAN_EFF_FLAG |
+ (sk_addr->src.addr & 0xff) |
+ ((sk_addr->priority & 0x7) << 26);
+ if (pgn_is_pdu1(sk_addr->pgn))
+ canid |= ((sk_addr->pgn & 0x3ff00) << 8) |
+ ((sk_addr->dst.addr & 0xff) << 8);
+ else
+ canid |= ((sk_addr->pgn & 0x3ffff) << 8);
+
+ msg->can_id = canid;
+ msg->can_dlc = dlc;
+
+ /* set net_device */
+ ret = -ENODEV;
+ if (!skb->dev) {
+ if (!sk_addr->ifindex)
+ goto failed;
+ netdev = dev_get_by_index(&init_net, sk_addr->ifindex);
+ if (!netdev)
+ goto failed;
+ skb->dev = netdev;
+ }
+
+ /* fix the 'always free' policy of can_send */
+ skb = skb_get(skb);
+ ret = can_send(skb, 1);
+ if (!ret) {
+ /* free when can_send succeeded */
+ kfree_skb(skb);
+ /* is this necessary ? */
+ ret = RESULT_STOP;
+ }
+failed:
+ if (netdev)
+ dev_put(netdev);
+ return ret;
+}
+
+static int j1939_send_normalize(struct sk_buff *skb)
+{
+ struct j1939_sk_buff_cb *cb = (void *)skb->cb;
+ struct j1939_segment *jseg;
+ struct addr_ent *paddr;
+ struct j1939_ecu *ecu;
+ int ret = 0;
+
+ /* apply sanity checks */
+ cb->pgn &= (pgn_is_pdu1(cb->pgn)) ? 0x3ff00 : 0x3ffff;
+ if (cb->priority > 7)
+ cb->priority = 6;
+
+ /* verify source */
+ if (!cb->ifindex)
+ return -ENETUNREACH;
+ jseg = j1939_segment_find(cb->ifindex);
+ if (!jseg)
+ return -ENETUNREACH;
+ read_lock_bh(&jseg->lock);
+ /* verify source */
+ if (cb->src.name) {
+ ecu = j1939_ecu_find_by_name(cb->src.name, cb->ifindex);
+ cb->src.flags = ecu ? ecu->flags : 0;
+ if (ecu)
+ put_j1939_ecu(ecu);
+ } else if (j1939_address_is_unicast(cb->src.addr)) {
+ paddr = &jseg->ents[cb->src.addr];
+ cb->src.flags = paddr->flags;
+ } else if (cb->src.addr == J1939_IDLE_ADDR) {
+ /* allow always */
+ cb->src.flags = ECUFLAG_LOCAL;
+ } else {
+ /* J1939_NO_ADDR */
+ cb->src.flags = 0;
+ }
+ if (cb->src.flags & ECUFLAG_REMOTE) {
+ ret = -EREMOTE;
+ goto failed;
+ } else if (!(cb->src.flags & ECUFLAG_LOCAL)) {
+ ret = -EADDRNOTAVAIL;
+ goto failed;
+ }
+
+ /* verify destination */
+ if (cb->dst.name) {
+ ecu = j1939_ecu_find_by_name(cb->dst.name, cb->ifindex);
+ if (!ecu) {
+ ret = -EADDRNOTAVAIL;
+ goto failed;
+ }
+ cb->dst.flags = ecu->flags;
+ put_j1939_ecu(ecu);
+ } else if (cb->dst.addr == J1939_IDLE_ADDR) {
+ /* not a valid destination */
+ ret = -EADDRNOTAVAIL;
+ goto failed;
+ } else if (j1939_address_is_unicast(cb->dst.addr)) {
+ paddr = &jseg->ents[cb->dst.addr];
+ cb->dst.flags = paddr->flags;
+ } else {
+ cb->dst.flags = 0;
+ }
+
+ ret = 0;
+failed:
+ read_unlock_bh(&jseg->lock);
+ put_j1939_segment(jseg);
+ return ret;
+}
+
+/* TOPLEVEL interface */
+int j1939_recv(struct sk_buff *skb, int level)
+{
+ int ret;
+
+ /* this stack operates with fallthrough switch statement */
+ switch (level) {
+ default:
+ WARN_ONCE(1, "%s: unsupported level %i\n", __func__, level);
+ return 0;
+ case j1939_level_can:
+ ret = j1939_recv_address_claim(skb);
+ if (unlikely(ret))
+ break;
+ ret = j1939_recv_promisc(skb);
+ if (unlikely(ret))
+ break;
+ ret = j1939_recv_transport(skb);
+ if (unlikely(ret))
+ break;
+ case j1939_level_transport:
+ case j1939_level_sky:
+ ret = j1939_recv_distribute(skb);
+ break;
+ }
+ if (ret == RESULT_STOP)
+ return 0;
+ return ret;
+
+}
+EXPORT_SYMBOL_GPL(j1939_recv);
+
+int j1939_send(struct sk_buff *skb, int level)
+{
+ int ret;
+ struct sock *sk = NULL;
+
+ /* this stack operates with fallthrough switch statement */
+ switch (level) {
+ default:
+ WARN_ONCE(1, "%s: unsupported level %i\n", __func__, level);
+ case j1939_level_sky:
+ sk = skb->sk;
+ if (sk)
+ sock_hold(sk);
+ ret = j1939_send_normalize(skb);
+ if (unlikely(ret))
+ break;
+ ret = j1939_send_transport(skb);
+ if (unlikely(ret))
+ break;
+ case j1939_level_transport:
+ ret = j1939_send_address_claim(skb);
+ if (unlikely(ret))
+ break;
+ case j1939_level_can:
+ ret = j1939_send_can(skb);
+ if (RESULT_STOP == ret)
+ /* don't mark as stopped, it can't be better */
+ ret = 0;
+ break;
+ }
+ if (ret == RESULT_STOP)
+ ret = 0;
+ else if (!ret && sk)
+ j1939_sock_pending_del(sk);
+ if (sk)
+ sock_put(sk);
+ return ret;
+
+}
+EXPORT_SYMBOL_GPL(j1939_send);
+
+/* NETDEV MANAGEMENT */
+
+#define J1939_CAN_ID CAN_EFF_FLAG
+#define J1939_CAN_MASK (CAN_EFF_FLAG | CAN_RTR_FLAG)
+int j1939_segment_attach(struct net_device *netdev)
+{
+ int ret;
+ struct j1939_segment *jseg;
+
+ if (!netdev)
+ return -ENODEV;
+ if (netdev->type != ARPHRD_CAN)
+ return -EAFNOSUPPORT;
+
+ ret = j1939_segment_register(netdev);
+ if (ret < 0)
+ goto fail_register;
+ jseg = j1939_segment_find(netdev->ifindex);
+ ret = can_rx_register(netdev, J1939_CAN_ID, J1939_CAN_MASK,
+ j1939_can_recv, jseg, "j1939");
+ if (ret < 0)
+ goto fail_can_rx;
+ return 0;
+
+fail_can_rx:
+ j1939_segment_unregister(jseg);
+ put_j1939_segment(jseg);
+fail_register:
+ return ret;
+}
+
+int j1939_segment_detach(struct net_device *netdev)
+{
+ struct j1939_segment *jseg;
+
+ BUG_ON(!netdev);
+ jseg = j1939_segment_find(netdev->ifindex);
+ if (!jseg)
+ return -EHOSTDOWN;
+ can_rx_unregister(netdev, J1939_CAN_ID, J1939_CAN_MASK,
+ j1939_can_recv, jseg);
+ j1939_segment_unregister(jseg);
+ put_j1939_segment(jseg);
+ j1939sk_netdev_event(netdev->ifindex, EHOSTDOWN);
+ return 0;
+}
+
+static int j1939_notifier(struct notifier_block *nb,
+ unsigned long msg, void *data)
+{
+ struct net_device *netdev = (struct net_device *)data;
+ struct j1939_segment *jseg;
+
+ if (!net_eq(dev_net(netdev), &init_net))
+ return NOTIFY_DONE;
+
+ if (netdev->type != ARPHRD_CAN)
+ return NOTIFY_DONE;
+
+ switch (msg) {
+ case NETDEV_UNREGISTER:
+ jseg = j1939_segment_find(netdev->ifindex);
+ if (!jseg)
+ break;
+ j1939_segment_unregister(jseg);
+ j1939sk_netdev_event(netdev->ifindex, ENODEV);
+ break;
+
+ case NETDEV_DOWN:
+ j1939sk_netdev_event(netdev->ifindex, ENETDOWN);
+ break;
+ }
+
+ return NOTIFY_DONE;
+}
+
+/* MODULE interface */
+
+static __init int j1939_module_init(void)
+{
+ int ret;
+
+ pr_info("can: SAE J1939\n");
+
+ ret = j1939_proc_module_init();
+ if (ret < 0)
+ goto fail_proc;
+
+ s.notifier.notifier_call = j1939_notifier;
+ register_netdevice_notifier(&s.notifier);
+
+ ret = j1939bus_module_init();
+ if (ret < 0)
+ goto fail_bus;
+ ret = j1939sk_module_init();
+ if (ret < 0)
+ goto fail_sk;
+ ret = j1939tp_module_init();
+ if (ret < 0)
+ goto fail_tp;
+ return 0;
+
+ j1939tp_module_exit();
+fail_tp:
+ j1939sk_module_exit();
+fail_sk:
+ j1939bus_module_exit();
+fail_bus:
+ unregister_netdevice_notifier(&s.notifier);
+
+ j1939_proc_module_exit();
+fail_proc:
+ return ret;
+}
+
+static __exit void j1939_module_exit(void)
+{
+ j1939tp_module_exit();
+ j1939sk_module_exit();
+ j1939bus_module_exit();
+
+ unregister_netdevice_notifier(&s.notifier);
+
+ j1939_proc_module_exit();
+}
+
+module_init(j1939_module_init);
+module_exit(j1939_module_exit);
diff --git a/net/can/j1939/proc.c b/net/can/j1939/proc.c
new file mode 100644
index 0000000..76acfa0
--- /dev/null
+++ b/net/can/j1939/proc.c
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2010-2011 EIA Electronics
+ *
+ * Authors:
+ * Kurt Van Dijck <kurt.van.dijck-/BeEPy95v10@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the version 2 of the GNU General Public License
+ * as published by the Free Software Foundation
+ */
+
+#include <linux/version.h>
+#include <linux/string.h>
+#include <linux/seq_file.h>
+#include <linux/proc_fs.h>
+#include <linux/uaccess.h>
+
+#include "j1939-priv.h"
+
+const char j1939_procname[] = "can-j1939";
+
+static struct proc_dir_entry *rootdir;
+
+static int j1939_proc_open(struct inode *inode, struct file *file)
+{
+ struct proc_dir_entry *pde = PDE(inode);
+ int (*fn)(struct seq_file *sqf, void *v) = pde->data;
+
+ return single_open(file, fn, pde);
+}
+
+/* copied from fs/proc/generic.c */
+static ssize_t
+proc_file_write(struct file *file, const char __user *buffer,
+ size_t count, loff_t *ppos)
+{
+ struct inode *inode = file->f_path.dentry->d_inode;
+ struct proc_dir_entry *dp;
+
+ dp = PDE(inode);
+
+ if (!dp->write_proc)
+ return -EIO;
+
+ /* FIXME: does this routine need ppos? probably... */
+ return dp->write_proc(file, buffer, count, dp->data);
+}
+
+static const struct file_operations j1939_proc_ops = {
+ .owner = THIS_MODULE,
+ .open = j1939_proc_open,
+ .read = seq_read,
+ .write = proc_file_write,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+int j1939_proc_add(const char *file,
+ int (*seq_show)(struct seq_file *sqf, void *v),
+ write_proc_t write)
+{
+ struct proc_dir_entry *pde;
+ int mode = 0;
+
+ if (seq_show)
+ mode |= 0444;
+ if (write)
+ mode |= 0200;
+
+ if (!rootdir)
+ return -ENODEV;
+ pde = proc_create(file, mode, rootdir, &j1939_proc_ops);
+ if (!pde)
+ goto fail_create;
+ pde->data = seq_show;
+ pde->write_proc = write;
+ return 0;
+
+fail_create:
+ return -ENOENT;
+}
+EXPORT_SYMBOL(j1939_proc_add);
+
+void j1939_proc_remove(const char *file)
+{
+ remove_proc_entry(file, rootdir);
+}
+EXPORT_SYMBOL(j1939_proc_remove);
+
+__init int j1939_proc_module_init(void)
+{
+ /* create /proc/net/can directory */
+ rootdir = proc_mkdir(j1939_procname, init_net.proc_net);
+ if (!rootdir)
+ return -EINVAL;
+ return 0;
+}
+
+void j1939_proc_module_exit(void)
+{
+ if (rootdir)
+ proc_net_remove(&init_net, j1939_procname);
+}
+
diff --git a/net/can/j1939/promisc.c b/net/can/j1939/promisc.c
new file mode 100644
index 0000000..14be755
--- /dev/null
+++ b/net/can/j1939/promisc.c
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2010-2011 EIA Electronics
+ *
+ * Authors:
+ * Kurt Van Dijck <kurt.van.dijck-/BeEPy95v10@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the version 2 of the GNU General Public License
+ * as published by the Free Software Foundation
+ */
+
+#include <linux/version.h>
+#include <linux/skbuff.h>
+#include <linux/sysctl.h>
+#include "j1939-priv.h"
+
+static atomic_t n_promisc = ATOMIC_INIT(0);
+
+void j1939_get_promisc_receiver(int ifindex)
+{
+ atomic_inc(&n_promisc);
+}
+EXPORT_SYMBOL_GPL(j1939_get_promisc_receiver);
+
+void j1939_put_promisc_receiver(int ifindex)
+{
+ atomic_dec(&n_promisc);
+}
+EXPORT_SYMBOL_GPL(j1939_put_promisc_receiver);
+
+int j1939_recv_promisc(struct sk_buff *skb)
+{
+ struct j1939_sk_buff_cb *cb = (void *)skb->cb;
+
+ if ((cb->src.flags & ECUFLAG_REMOTE) &&
+ (cb->dst.flags & ECUFLAG_REMOTE)) {
+ if (!atomic_read(&n_promisc))
+ /* stop receive path */
+ return RESULT_STOP;
+ }
+ return 0;
+}
+
diff --git a/net/can/j1939/rtnl.c b/net/can/j1939/rtnl.c
new file mode 100644
index 0000000..851060f
--- /dev/null
+++ b/net/can/j1939/rtnl.c
@@ -0,0 +1,308 @@
+/*
+ * Copyright (c) 2011 EIA Electronics
+ *
+ * Authors:
+ * Kurt Van Dijck <kurt.van.dijck-/BeEPy95v10@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the version 2 of the GNU General Public License
+ * as published by the Free Software Foundation
+ */
+
+/*
+ * j1939-rtnl.c - netlink addressing interface
+ */
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/if_arp.h>
+
+#include "j1939-priv.h"
+
+static const struct nla_policy j1939_ifa_policy[IFA_J1939_MAX] = {
+ [IFA_J1939_ADDR] = { .type = NLA_U8, },
+ [IFA_J1939_NAME] = { .type = NLA_U64, },
+};
+
+int j1939rtnl_del_addr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
+{
+ int ret;
+ struct ifaddrmsg *ifm;
+ struct j1939_segment *jseg;
+ uint8_t jaddr = J1939_NO_ADDR;
+ uint64_t jname = J1939_NO_NAME;
+
+ struct nlattr *nla, *tb[IFA_J1939_MAX];
+
+ if (!net_eq(sock_net(skb->sk), &init_net))
+ return -EINVAL;
+
+ nla = nlmsg_find_attr(nlh, sizeof(*ifm), IFA_LOCAL);
+ if (!nla)
+ return -EINVAL;
+
+ nla_parse_nested(tb, IFA_J1939_MAX-1, nla, j1939_ifa_policy);
+ if (tb[IFA_J1939_ADDR])
+ jaddr = nla_get_u8(tb[IFA_J1939_ADDR]);
+ if (tb[IFA_J1939_NAME])
+ jname = be64_to_cpu(nla_get_u64(tb[IFA_J1939_NAME]));
+
+ ifm = nlmsg_data(nlh);
+ jseg = j1939_segment_find(ifm->ifa_index);
+ if (!jseg)
+ return -EHOSTDOWN;
+
+ ret = 0;
+ if (j1939_address_is_unicast(jaddr)) {
+ struct addr_ent *ent;
+
+ ent = &jseg->ents[jaddr];
+ write_lock_bh(&jseg->lock);
+ if (!ent->flags)
+ ret = -EADDRNOTAVAIL;
+ else if (!(ent->flags & ECUFLAG_LOCAL))
+ ret = -EREMOTE;
+ else
+ ent->flags = 0;
+ write_unlock_bh(&jseg->lock);
+ } else if (jname) {
+ struct j1939_ecu *ecu;
+
+ ecu = j1939_ecu_find_by_name(jname, ifm->ifa_index);
+ if (ecu) {
+ if (ecu->flags & ECUFLAG_LOCAL) {
+ j1939_ecu_unregister(ecu);
+ put_j1939_ecu(ecu);
+ } else {
+ ret = -EREMOTE;
+ }
+ } else {
+ ret = -ENODEV;
+ }
+ }
+ put_j1939_segment(jseg);
+ return ret;
+}
+
+int j1939rtnl_new_addr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
+{
+ struct ifaddrmsg *ifm;
+ struct j1939_segment *jseg;
+ uint8_t jaddr = J1939_NO_ADDR;
+ uint64_t jname = J1939_NO_NAME;
+ struct addr_ent *ent;
+ int ret;
+ struct nlattr *nla, *tb[IFA_J1939_MAX];
+
+ if (!net_eq(sock_net(skb->sk), &init_net))
+ return -EINVAL;
+
+ nla = nlmsg_find_attr(nlh, sizeof(*ifm), IFA_LOCAL);
+ if (!nla)
+ return -EINVAL;
+
+ ifm = nlmsg_data(nlh);
+ jseg = j1939_segment_find(ifm->ifa_index);
+ if (!jseg)
+ return -EHOSTDOWN;
+
+ nla_parse_nested(tb, IFA_J1939_MAX-1, nla, j1939_ifa_policy);
+ if (tb[IFA_J1939_ADDR])
+ jaddr = nla_get_u8(tb[IFA_J1939_ADDR]);
+ if (tb[IFA_J1939_NAME])
+ jname = be64_to_cpu(nla_get_u64(tb[IFA_J1939_NAME]));
+
+
+ ret = 0;
+ if (j1939_address_is_unicast(jaddr)) {
+ ent = &jseg->ents[jaddr];
+ write_lock_bh(&jseg->lock);
+ if ((ent->ecu && (ent->ecu->flags & ECUFLAG_REMOTE)) ||
+ (ent->flags & ECUFLAG_REMOTE))
+ ret = -EREMOTE;
+ else
+ ent->flags |= ECUFLAG_LOCAL;
+ write_unlock_bh(&jseg->lock);
+ } else if (jname) {
+ struct j1939_ecu *ecu;
+
+ ecu = j1939_ecu_get_register(jname, ifm->ifa_index,
+ ECUFLAG_LOCAL, 0);
+ if (IS_ERR(ecu))
+ ret = PTR_ERR(ecu);
+ else
+ put_j1939_ecu(ecu);
+ }
+ put_j1939_segment(jseg);
+ return ret;
+}
+
+static int j1939rtnl_fill_ifaddr(struct sk_buff *skb, int ifindex,
+ uint8_t addr, uint64_t name, int j1939_flags,
+ u32 pid, u32 seq, int event, unsigned int flags)
+{
+ struct ifaddrmsg *ifm;
+ struct nlmsghdr *nlh;
+ struct nlattr *nla;
+
+ nlh = nlmsg_put(skb, pid, seq, event, sizeof(*ifm), flags);
+ if (nlh == NULL)
+ return -EMSGSIZE;
+
+ ifm = nlmsg_data(nlh);
+ ifm->ifa_family = AF_CAN;
+ ifm->ifa_prefixlen = CAN_J1939;
+ ifm->ifa_flags = name ? 0 : IFA_F_PERMANENT;
+ ifm->ifa_scope = RT_SCOPE_LINK;
+ ifm->ifa_index = ifindex;
+
+ nla = nla_nest_start(skb, IFA_LOCAL);
+ if (j1939_address_is_unicast(addr))
+ NLA_PUT_U8(skb, IFA_J1939_ADDR, addr);
+ if (name)
+ NLA_PUT_U64(skb, IFA_J1939_NAME, cpu_to_be64(name));
+ nla_nest_end(skb, nla);
+
+ return nlmsg_end(skb, nlh);
+
+nla_put_failure:
+ nlmsg_cancel(skb, nlh);
+ return -EMSGSIZE;
+}
+
+int j1939rtnl_dump_addr(struct sk_buff *skb, struct netlink_callback *cb)
+{
+ int ndev, addr, ret, sa;
+ struct net_device *netdev;
+ struct j1939_segment *jseg;
+ struct j1939_ecu *ecu;
+ struct addr_ent *ent;
+
+ if (!net_eq(sock_net(skb->sk), &init_net))
+ return 0;
+
+ ndev = 0;
+ for_each_netdev(&init_net, netdev) {
+ ++ndev;
+ if (ndev < cb->args[1])
+ continue;
+ if (netdev->type != ARPHRD_CAN)
+ continue;
+
+ jseg = j1939_segment_find(netdev->ifindex);
+ if (!jseg)
+ continue;
+
+ read_lock_bh(&jseg->lock);
+ for (addr = cb->args[2]; addr < J1939_IDLE_ADDR; ++addr) {
+ ent = &jseg->ents[addr];
+ if (!ent->flags)
+ continue;
+ ret = j1939rtnl_fill_ifaddr(skb, netdev->ifindex, addr,
+ 0, ent->flags, NETLINK_CB(cb->skb).pid,
+ cb->nlh->nlmsg_seq, RTM_NEWADDR,
+ NLM_F_MULTI);
+ if (ret < 0) {
+ read_unlock_bh(&jseg->lock);
+ goto done;
+ }
+ cb->args[2] = addr + 1;
+ }
+
+ if (addr > J1939_IDLE_ADDR)
+ addr = J1939_IDLE_ADDR;
+ list_for_each_entry(ecu, &jseg->ecus, list) {
+ if (addr++ < cb->args[2])
+ continue;
+ if (!(ecu->flags & ECUFLAG_LOCAL))
+ continue;
+ sa = ecu->sa;
+ if (ecu->parent->ents[sa].ecu != ecu)
+ sa = J1939_IDLE_ADDR;
+ ret = j1939rtnl_fill_ifaddr(skb, netdev->ifindex,
+ sa, ecu->name, ecu->flags,
+ NETLINK_CB(cb->skb).pid,
+ cb->nlh->nlmsg_seq, RTM_NEWADDR,
+ NLM_F_MULTI);
+ if (ret < 0) {
+ read_unlock_bh(&jseg->lock);
+ goto done;
+ }
+ cb->args[2] = addr;
+ }
+ read_unlock_bh(&jseg->lock);
+ /* reset first address for device */
+ cb->args[2] = 0;
+ }
+ ++ndev;
+done:
+ cb->args[1] = ndev;
+
+ return skb->len;
+}
+
+/*
+ * rtnl_link_ops
+ */
+
+static const struct nla_policy j1939_ifla_policy[IFLA_J1939_MAX] = {
+ [IFLA_J1939_ENABLE] = { .type = NLA_U8, },
+};
+
+static size_t j1939_get_link_af_size(const struct net_device *dev)
+{
+ return nla_policy_len(j1939_ifla_policy, IFLA_J1939_MAX-1);
+}
+
+static int j1939_validate_link_af(const struct net_device *dev,
+ const struct nlattr *nla)
+{
+ return nla_validate_nested(nla, IFLA_J1939_MAX-1, j1939_ifla_policy);
+}
+
+static int j1939_fill_link_af(struct sk_buff *skb, const struct net_device *dev)
+{
+ struct j1939_segment *jseg;
+
+ if (!dev)
+ return -ENODEV;
+ jseg = j1939_segment_find(dev->ifindex);
+ if (jseg)
+ put_j1939_segment(jseg);
+ NLA_PUT_U8(skb, IFLA_J1939_ENABLE, jseg ? 1 : 0);
+ return 0;
+
+nla_put_failure:
+ return -EMSGSIZE;
+}
+
+static int j1939_set_link_af(struct net_device *dev, const struct nlattr *nla)
+{
+ int ret;
+ struct nlattr *tb[IFLA_J1939_MAX];
+
+ ret = nla_parse_nested(tb, IFLA_J1939_MAX-1, nla, j1939_ifla_policy);
+ if (ret < 0)
+ return ret;
+
+ if (tb[IFLA_J1939_ENABLE]) {
+ if (nla_get_u8(tb[IFLA_J1939_ENABLE]))
+ ret = j1939_segment_attach(dev);
+ else
+ ret = j1939_segment_detach(dev);
+ if (ret < 0)
+ return ret;
+ }
+ return 0;
+}
+
+const struct rtnl_af_ops j1939_rtnl_af_ops = {
+ .family = AF_CAN,
+ .fill_link_af = j1939_fill_link_af,
+ .get_link_af_size = j1939_get_link_af_size,
+ .validate_link_af = j1939_validate_link_af,
+ .set_link_af = j1939_set_link_af,
+};
+
diff --git a/net/can/j1939/socket.c b/net/can/j1939/socket.c
new file mode 100644
index 0000000..e41cb31
--- /dev/null
+++ b/net/can/j1939/socket.c
@@ -0,0 +1,969 @@
+/*
+ * Copyright (c) 2010-2011 EIA Electronics
+ *
+ * Authors:
+ * Kurt Van Dijck <kurt.van.dijck-/BeEPy95v10@public.gmane.org>
+ * Pieter Beyens <pieter.beyens-/BeEPy95v10@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the version 2 of the GNU General Public License
+ * as published by the Free Software Foundation
+ */
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/socket.h>
+#include <linux/list.h>
+#include <linux/if_arp.h>
+#include <net/tcp_states.h>
+
+#include <linux/can/core.h>
+#include <linux/can/j1939.h>
+#include "j1939-priv.h"
+
+struct j1939_sock {
+ struct sock sk; /* must be first to skip with memset */
+ struct list_head list;
+
+ int state;
+ #define JSK_BOUND BIT(0)
+ #define JSK_CONNECTED BIT(1)
+ #define PROMISC BIT(2)
+ #define RECV_OWN BIT(3)
+
+ struct {
+ name_t src, dst;
+ pgn_t pgn;
+
+ uint8_t sa, da;
+ } addr;
+
+ struct j1939_filter *filters;
+ int nfilters;
+
+ int skb_pending;
+ spinlock_t lock;
+ wait_queue_head_t waitq;
+};
+
+static inline struct j1939_sock *j1939_sk(const struct sock *sk)
+{
+ return container_of(sk, struct j1939_sock, sk);
+}
+
+/* skb_pending issues */
+static inline int j1939_sock_pending_add_first(struct sock *sk)
+{
+ int saved;
+ struct j1939_sock *jsk = j1939_sk(sk);
+
+ spin_lock_bh(&jsk->lock);
+ if (!jsk->skb_pending) {
+ ++jsk->skb_pending;
+ saved = 1;
+ } else
+ saved = 0;
+ spin_unlock_bh(&jsk->lock);
+ return saved;
+}
+
+static inline void j1939_sock_pending_add(struct sock *sk)
+{
+ struct j1939_sock *jsk = j1939_sk(sk);
+
+ spin_lock_bh(&jsk->lock);
+ ++jsk->skb_pending;
+ spin_unlock_bh(&jsk->lock);
+}
+
+void j1939_sock_pending_del(struct sock *sk)
+{
+ struct j1939_sock *jsk = j1939_sk(sk);
+ int saved;
+
+ spin_lock_bh(&jsk->lock);
+ --jsk->skb_pending;
+ saved = jsk->skb_pending;
+ spin_unlock_bh(&jsk->lock);
+ if (!saved)
+ wake_up(&jsk->waitq);
+}
+
+
+static inline int j1939_no_address(const struct sock *sk)
+{
+ const struct j1939_sock *jsk = j1939_sk(sk);
+ return (jsk->addr.sa == J1939_NO_ADDR) && !jsk->addr.src;
+}
+
+/*
+ * list of sockets
+ */
+static struct {
+ struct mutex lock;
+ struct list_head socks;
+} s;
+
+/* matches skb control buffer (addr) with a j1939 filter */
+static inline int packet_match(const struct j1939_sk_buff_cb *cb,
+ const struct j1939_filter *f, int nfilter)
+{
+ if (!nfilter)
+ /* receive all when no filters are assigned */
+ return 1;
+ /*
+ * Filters relying on the addr for static addressing _should_ get
+ * packets from dynamic addressed ECU's too if they match their SA.
+ * Sockets using dynamic addressing in their filters should not set it.
+ */
+ for (; nfilter; ++f, --nfilter) {
+ if ((cb->pgn & f->pgn_mask) != (f->pgn & f->pgn_mask))
+ continue;
+ if ((cb->src.addr & f->addr_mask) != (f->addr & f->addr_mask))
+ continue;
+ if ((cb->src.name & f->name_mask) != (f->name & f->name_mask))
+ continue;
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * callback per socket, called from filter infrastructure
+ */
+static void j1939sk_recv_skb(struct sk_buff *oskb, void *data)
+{
+ struct sk_buff *skb;
+ struct j1939_sock *jsk = (struct j1939_sock *)data;
+ struct j1939_sk_buff_cb *cb = (void *)oskb->cb;
+
+ if (jsk->sk.sk_bound_dev_if && (jsk->sk.sk_bound_dev_if != cb->ifindex))
+ /* this socket does not take packets from this iface */
+ return;
+ if (!(jsk->state & PROMISC)) {
+ if (cb->dst.flags & ECUFLAG_REMOTE)
+ /*
+ * this msg was destined for an ECU associated
+ * with this socket
+ */
+ return;
+ if (jsk->addr.src) {
+ if (cb->dst.name &&
+ (cb->dst.name != jsk->addr.src))
+ /*
+ * the msg is not destined for the name
+ * that the socket is bound to
+ */
+ return;
+ } else if (j1939_address_is_unicast(jsk->addr.sa)) {
+ if (j1939_address_is_unicast(cb->dst.addr) &&
+ (cb->dst.addr != jsk->addr.sa))
+ /*
+ * the msg is not destined for the name
+ * that the socket is bound to
+ */
+ return;
+ }
+ }
+
+ if ((oskb->sk == &jsk->sk) && !(jsk->state & RECV_OWN))
+ /* own message */
+ return;
+
+ if (!packet_match(cb, jsk->filters, jsk->nfilters))
+ return;
+
+ skb = skb_clone(oskb, GFP_ATOMIC);
+ if (!skb) {
+ j1939_warning("skb clone failed\n");
+ return;
+ }
+ cb = (void *)skb->cb;
+ cb->msg_flags &= ~(MSG_DONTROUTE | MSG_CONFIRM);
+ if (oskb->sk)
+ cb->msg_flags |= MSG_DONTROUTE;
+ if (oskb->sk == &jsk->sk)
+ cb->msg_flags |= MSG_CONFIRM;
+
+ skb->sk = &jsk->sk;
+ if (sock_queue_rcv_skb(&jsk->sk, skb) < 0)
+ kfree_skb(skb);
+}
+
+static int j1939sk_init(struct sock *sk)
+{
+ struct j1939_sock *jsk = j1939_sk(sk);
+
+ INIT_LIST_HEAD(&jsk->list);
+ spin_lock_init(&jsk->lock);
+ init_waitqueue_head(&jsk->waitq);
+ jsk->sk.sk_priority = j1939_to_sk_priority(6);
+ jsk->sk.sk_reuse = 1; /* per default */
+ jsk->addr.sa = J1939_NO_ADDR;
+ jsk->addr.da = J1939_NO_ADDR;
+ return 0;
+}
+
+/*
+ * helper: return <0 for error, >0 for error to notify
+ */
+static int j1939sk_bind_netdev_helper(struct socket *sock)
+{
+ struct j1939_sock *jsk = j1939_sk(sock->sk);
+ int ret;
+ struct net_device *netdev;
+ struct j1939_segment *jseg;
+
+ if (!jsk->sk.sk_bound_dev_if)
+ return 0;
+ ret = 0;
+
+ netdev = dev_get_by_index(&init_net, jsk->sk.sk_bound_dev_if);
+ if (!netdev) {
+ ret = -ENODEV;
+ goto fail_netdev;
+ }
+
+ /* no need to test for CAN device,
+ * implicitely done by j1939_segment
+ */
+ jseg = j1939_segment_find(netdev->ifindex);
+ if (!jseg) {
+ ret = -EHOSTDOWN;
+ goto fail_segment;
+ }
+
+ if (!(netdev->flags & IFF_UP)) {
+ sock->sk->sk_err = ENETDOWN;
+ sock->sk->sk_error_report(sock->sk);
+ }
+ put_j1939_segment(jseg);
+fail_segment:
+ dev_put(netdev);
+fail_netdev:
+ return ret;
+}
+
+static int j1939sk_bind(struct socket *sock, struct sockaddr *uaddr, int len)
+{
+ struct sockaddr_can *addr = (struct sockaddr_can *)uaddr;
+ struct j1939_sock *jsk = j1939_sk(sock->sk);
+ struct j1939_ecu *ecu = NULL;
+ int ret, old_state;
+
+ if (len < required_size(can_addr.j1939, *addr))
+ return -EINVAL;
+ if (addr->can_family != AF_CAN)
+ return -EINVAL;
+
+ /* lock s.lock first, to avoid circular lock dependancy */
+ mutex_lock(&s.lock);
+ lock_sock(sock->sk);
+ if (jsk->state & JSK_BOUND) {
+ ret = -EBUSY;
+ if (addr->can_ifindex &&
+ (addr->can_ifindex != jsk->sk.sk_bound_dev_if))
+ goto fail_locked;
+ /*
+ * do not allow to change addres after first bind(),
+ * (it would require updating the j1939_ecu list)
+ * but allow the change SA when using dynaddr,
+ * and allow to change PGN
+ */
+ if (!jsk->addr.src ||
+ (jsk->addr.src != addr->can_addr.j1939.name) ||
+ (jsk->addr.pgn != addr->can_addr.j1939.pgn))
+ goto fail_locked;
+ /* set to be able to send address claims */
+ jsk->addr.sa = addr->can_addr.j1939.addr;
+ /* since this socket is bound already, we can skip a lot */
+ release_sock(sock->sk);
+ mutex_unlock(&s.lock);
+ return 0;
+ }
+
+ /* do netdev */
+ if (jsk->sk.sk_bound_dev_if && addr->can_ifindex &&
+ (jsk->sk.sk_bound_dev_if != addr->can_ifindex)) {
+ ret = -EBADR;
+ goto fail_locked;
+ }
+ if (!jsk->sk.sk_bound_dev_if)
+ jsk->sk.sk_bound_dev_if = addr->can_ifindex;
+
+ ret = j1939sk_bind_netdev_helper(sock);
+ if (ret < 0)
+ goto fail_locked;
+
+ /* bind name/addr */
+ if (addr->can_addr.j1939.name) {
+ ecu = j1939_ecu_find_by_name(addr->can_addr.j1939.name,
+ jsk->sk.sk_bound_dev_if);
+ if (!ecu) {
+ ret = -EADDRNOTAVAIL;
+ goto fail_locked;
+ } else if (ecu->flags & ECUFLAG_REMOTE) {
+ ret = -EREMOTE;
+ goto fail_with_ecu;
+ } else if (jsk->sk.sk_bound_dev_if != ecu->parent->ifindex) {
+ ret = -EHOSTUNREACH;
+ goto fail_with_ecu;
+ }
+ jsk->addr.src = ecu->name;
+ jsk->addr.sa = addr->can_addr.j1939.addr;
+ } else if (j1939_address_is_unicast(addr->can_addr.j1939.addr)) {
+ struct j1939_segment *jseg;
+ struct addr_ent *paddr;
+ int flags;
+
+ /* static addressing, netdev is required */
+ if (!jsk->sk.sk_bound_dev_if) {
+ ret = -EINVAL;
+ goto fail_locked;
+ }
+ jseg = j1939_segment_find(jsk->sk.sk_bound_dev_if);
+ if (!jseg) {
+ ret = -ENETUNREACH;
+ goto fail_locked;
+ }
+ paddr = &jseg->ents[addr->can_addr.j1939.addr];
+ ret = 0;
+ read_lock_bh(&jseg->lock);
+ flags = paddr->flags;
+ read_unlock_bh(&jseg->lock);
+ put_j1939_segment(jseg);
+ if (!(flags & ECUFLAG_LOCAL)) {
+ ret = -EADDRNOTAVAIL;
+ goto fail_locked;
+ }
+ jsk->addr.sa = addr->can_addr.j1939.addr;
+ } else if (addr->can_addr.j1939.addr == J1939_IDLE_ADDR) {
+ /* static addressing, netdev is required */
+ if (!jsk->sk.sk_bound_dev_if) {
+ ret = -EINVAL;
+ goto fail_locked;
+ }
+ jsk->addr.sa = addr->can_addr.j1939.addr;
+ } else {
+ /* no name, no addr */
+ }
+
+ /* set default transmit pgn/priority */
+ jsk->addr.pgn = addr->can_addr.j1939.pgn;
+
+ old_state = jsk->state;
+ jsk->state |= JSK_BOUND;
+
+ if (!(old_state & (JSK_BOUND | JSK_CONNECTED))) {
+ list_add_tail(&jsk->list, &s.socks);
+ j1939_recv_add(jsk, j1939sk_recv_skb);
+ }
+
+ ret = 0;
+
+fail_with_ecu:
+ if (ecu && !IS_ERR(ecu))
+ put_j1939_ecu(ecu);
+fail_locked:
+ release_sock(sock->sk);
+ mutex_unlock(&s.lock);
+ return ret;
+}
+
+static int j1939sk_connect(struct socket *sock, struct sockaddr *uaddr,
+ int len, int flags)
+{
+ int ret, old_state;
+ struct sockaddr_can *addr = (struct sockaddr_can *)uaddr;
+ struct j1939_sock *jsk = j1939_sk(sock->sk);
+ struct j1939_ecu *ecu;
+ int ifindex;
+
+ if (!uaddr)
+ return -EDESTADDRREQ;
+
+ if (len < required_size(can_addr.j1939, *addr))
+ return -EINVAL;
+ if (addr->can_family != AF_CAN)
+ return -EINVAL;
+
+ mutex_lock(&s.lock);
+ lock_sock(sock->sk);
+ if (jsk->state & JSK_CONNECTED) {
+ ret = -EISCONN;
+ goto fail_locked;
+ }
+
+ ifindex = jsk->sk.sk_bound_dev_if;
+ if (ifindex && addr->can_ifindex && (ifindex != addr->can_ifindex)) {
+ ret = -ECONNREFUSED;
+ goto fail_locked;
+ }
+ if (!ifindex)
+ ifindex = addr->can_ifindex;
+
+ /* lookup destination */
+ if (addr->can_addr.j1939.name) {
+ ecu = j1939_ecu_find_by_name(addr->can_addr.j1939.name,
+ ifindex);
+ if (!ecu) {
+ ret = -EADDRNOTAVAIL;
+ goto fail_locked;
+ }
+ if (ifindex && (ifindex != ecu->parent->ifindex)) {
+ ret = -EHOSTUNREACH;
+ goto fail_locked;
+ }
+ ifindex = ecu->parent->ifindex;
+ jsk->addr.dst = ecu->name;
+ jsk->addr.da = ecu->sa;
+ put_j1939_ecu(ecu);
+ } else {
+ /* broadcast */
+ jsk->addr.dst = 0;
+ jsk->addr.da = addr->can_addr.j1939.addr;
+ }
+ /*
+ * take a default source when not present, so connected sockets
+ * will stick to the same source ECU
+ */
+ if (!jsk->addr.src && !j1939_address_is_valid(jsk->addr.sa)) {
+ ecu = j1939_ecu_find_segment_default_tx(ifindex,
+ &jsk->addr.src, &jsk->addr.sa);
+ if (IS_ERR(ecu)) {
+ ret = PTR_ERR(ecu);
+ goto fail_locked;
+ }
+ put_j1939_ecu(ecu);
+ }
+
+ /* start assigning, no problem can occur at this point anymore */
+ jsk->sk.sk_bound_dev_if = ifindex;
+
+ if (!(jsk->state & JSK_BOUND) || !pgn_is_valid(jsk->addr.pgn)) {
+ /*
+ * bind() takes precedence over connect() for the
+ * pgn to use ourselve
+ */
+ jsk->addr.pgn = addr->can_addr.j1939.pgn;
+ }
+
+ old_state = jsk->state;
+ jsk->state |= JSK_CONNECTED;
+
+ if (!(old_state & (JSK_BOUND | JSK_CONNECTED))) {
+ list_add_tail(&jsk->list, &s.socks);
+ j1939_recv_add(jsk, j1939sk_recv_skb);
+ }
+ release_sock(sock->sk);
+ mutex_unlock(&s.lock);
+ return 0;
+
+fail_locked:
+ release_sock(sock->sk);
+ mutex_unlock(&s.lock);
+ return ret;
+}
+
+static void j1939sk_sock2sockaddr_can(struct sockaddr_can *addr,
+ const struct j1939_sock *jsk, int peer)
+{
+ addr->can_family = AF_CAN;
+ addr->can_ifindex = jsk->sk.sk_bound_dev_if;
+ addr->can_addr.j1939.name = peer ? jsk->addr.dst : jsk->addr.src;
+ addr->can_addr.j1939.pgn = jsk->addr.pgn;
+ addr->can_addr.j1939.addr = peer ? jsk->addr.da : jsk->addr.sa;
+}
+
+static int j1939sk_getname(struct socket *sock, struct sockaddr *uaddr,
+ int *len, int peer)
+{
+ struct sockaddr_can *addr = (struct sockaddr_can *)uaddr;
+ struct sock *sk = sock->sk;
+ struct j1939_sock *jsk = j1939_sk(sk);
+ int ret = 0;
+
+ lock_sock(sk);
+
+ if (peer && !(jsk->state & JSK_CONNECTED)) {
+ ret = -EADDRNOTAVAIL;
+ goto failure;
+ }
+
+ j1939sk_sock2sockaddr_can(addr, jsk, peer);
+ *len = sizeof(*addr);
+
+failure:
+ release_sock(sk);
+
+ return ret;
+}
+
+static int j1939sk_release(struct socket *sock)
+{
+ struct sock *sk = sock->sk;
+ struct j1939_sock *jsk;
+
+ if (!sk)
+ return 0;
+ jsk = j1939_sk(sk);
+ j1939_recv_remove(jsk, j1939sk_recv_skb);
+ mutex_lock(&s.lock);
+ list_del_init(&jsk->list);
+ mutex_unlock(&s.lock);
+
+ lock_sock(sk);
+ if (jsk->state & PROMISC)
+ j1939_put_promisc_receiver(jsk->sk.sk_bound_dev_if);
+
+ sock_orphan(sk);
+ sock->sk = NULL;
+
+ release_sock(sk);
+ sock_put(sk);
+
+ return 0;
+}
+
+static int j1939sk_setsockopt_flag(struct j1939_sock *jsk,
+ char __user *optval, unsigned int optlen, int flag)
+{
+ int tmp;
+
+ if (optlen != sizeof(tmp))
+ return -EINVAL;
+ if (copy_from_user(&tmp, optval, optlen))
+ return -EFAULT;
+ lock_sock(&jsk->sk);
+ if (tmp)
+ jsk->state |= flag;
+ else
+ jsk->state &= ~flag;
+ release_sock(&jsk->sk);
+ return tmp;
+}
+
+static int j1939sk_setsockopt(struct socket *sock, int level, int optname,
+ char __user *optval, unsigned int optlen)
+{
+ struct sock *sk = sock->sk;
+ struct j1939_sock *jsk = j1939_sk(sk);
+ int ret = 0, tmp, count;
+ struct j1939_filter *filters, *ofilters;
+
+ if (level != SOL_CAN_J1939)
+ return -EINVAL;
+
+ switch (optname) {
+ case SO_J1939_FILTER:
+ if (optval) {
+ if (optlen % sizeof(*filters) != 0)
+ return -EINVAL;
+ count = optlen / sizeof(*filters);
+ filters = kmalloc(optlen, GFP_KERNEL);
+ if (!filters)
+ return -ENOMEM;
+ if (copy_from_user(filters, optval, optlen)) {
+ kfree(filters);
+ return -EFAULT;
+ }
+ } else {
+ filters = NULL;
+ count = 0;
+ }
+
+ j1939_recv_suspend();
+ ofilters = jsk->filters;
+ jsk->filters = filters;
+ jsk->nfilters = count;
+ j1939_recv_resume();
+ if (ofilters)
+ kfree(ofilters);
+ break;
+ case SO_J1939_PROMISC:
+ tmp = jsk->state & PROMISC;
+ ret = j1939sk_setsockopt_flag(jsk, optval, optlen, PROMISC);
+ if (ret && !tmp)
+ j1939_get_promisc_receiver(jsk->sk.sk_bound_dev_if);
+ else if (!ret && tmp)
+ j1939_put_promisc_receiver(jsk->sk.sk_bound_dev_if);
+ ret = 0;
+ break;
+ case SO_J1939_RECV_OWN:
+ j1939sk_setsockopt_flag(jsk, optval, optlen, RECV_OWN);
+ break;
+ case SO_J1939_SEND_PRIO:
+ if (optlen != sizeof(tmp))
+ return -EINVAL;
+ if (copy_from_user(&tmp, optval, optlen))
+ return -EFAULT;
+ if ((tmp < 0) || (tmp > 7))
+ return -EDOM;
+ if ((tmp < 2) && !capable(CAP_NET_ADMIN))
+ return -EPERM;
+ lock_sock(&jsk->sk);
+ jsk->sk.sk_priority = j1939_to_sk_priority(tmp);
+ release_sock(&jsk->sk);
+ break;
+ default:
+ return -ENOPROTOOPT;
+ }
+
+ return ret;
+}
+
+static int j1939sk_getsockopt(struct socket *sock, int level, int optname,
+ char __user *optval, int __user *optlen)
+{
+ struct sock *sk = sock->sk;
+ struct j1939_sock *jsk = j1939_sk(sk);
+ int ret, ulen;
+ /* set defaults for using 'int' properties */
+ int tmp = 0;
+ int len = sizeof(tmp);
+ void *val = &tmp;
+
+ if (level != SOL_CAN_J1939)
+ return -EINVAL;
+ if (get_user(ulen, optlen))
+ return -EFAULT;
+ if (ulen < 0)
+ return -EINVAL;
+
+ lock_sock(&jsk->sk);
+ switch (optname) {
+ case SO_J1939_PROMISC:
+ tmp = (jsk->state & PROMISC) ? 1 : 0;
+ break;
+ case SO_J1939_RECV_OWN:
+ tmp = (jsk->state & RECV_OWN) ? 1 : 0;
+ break;
+ case SO_J1939_SEND_PRIO:
+ tmp = j1939_prio(jsk->sk.sk_priority);
+ break;
+ default:
+ ret = -ENOPROTOOPT;
+ goto no_copy;
+ }
+
+ /*
+ * copy to user, based on 'len' & 'val'
+ * but most sockopt's are 'int' properties, and have 'len' & 'val'
+ * left unchanged, but instead modified 'tmp'
+ */
+ if (len > ulen)
+ ret = -EFAULT;
+ else if (put_user(len, optlen))
+ ret = -EFAULT;
+ else if (copy_to_user(optval, val, len))
+ ret = -EFAULT;
+ else
+ ret = 0;
+no_copy:
+ release_sock(&jsk->sk);
+ return ret;
+}
+
+static int j1939sk_recvmsg(struct kiocb *iocb, struct socket *sock,
+ struct msghdr *msg, size_t size, int flags)
+{
+ struct sock *sk = sock->sk;
+ struct sk_buff *skb;
+ struct j1939_sk_buff_cb *sk_addr;
+ int ret = 0;
+
+ skb = skb_recv_datagram(sk, flags, 0, &ret);
+ if (!skb)
+ return ret;
+
+ if (size < skb->len)
+ msg->msg_flags |= MSG_TRUNC;
+ else
+ size = skb->len;
+
+ ret = memcpy_toiovec(msg->msg_iov, skb->data, size);
+ if (ret < 0)
+ goto failed_with_skb;
+
+ sock_recv_timestamp(msg, sk, skb);
+ sk_addr = (void *)skb->cb;
+
+ if (j1939_address_is_valid(sk_addr->dst.addr))
+ put_cmsg(msg, SOL_CAN_J1939, SCM_J1939_DEST_ADDR,
+ sizeof(sk_addr->dst.addr), &sk_addr->dst.addr);
+
+ if (sk_addr->dst.name)
+ put_cmsg(msg, SOL_CAN_J1939, SCM_J1939_DEST_NAME,
+ sizeof(sk_addr->dst.name), &sk_addr->dst.name);
+
+ put_cmsg(msg, SOL_CAN_J1939, SCM_J1939_PRIO,
+ sizeof(sk_addr->priority), &sk_addr->priority);
+
+ if (msg->msg_name) {
+ struct sockaddr_can *paddr = msg->msg_name;
+
+ msg->msg_namelen = required_size(can_addr.j1939, *paddr);
+ memset(msg->msg_name, 0, msg->msg_namelen);
+ paddr->can_family = AF_CAN;
+ paddr->can_ifindex = sk_addr->ifindex;
+ paddr->can_addr.j1939.name = sk_addr->src.name;
+ paddr->can_addr.j1939.addr = sk_addr->src.addr;
+ paddr->can_addr.j1939.pgn = sk_addr->pgn;
+ }
+
+ skb_free_datagram(sk, skb);
+
+ return size;
+
+failed_with_skb:
+ skb_kill_datagram(sk, skb, flags);
+ return ret;
+}
+
+static int j1939sk_sendmsg(struct kiocb *iocb, struct socket *sock,
+ struct msghdr *msg, size_t size)
+{
+ struct sock *sk = sock->sk;
+ struct j1939_sock *jsk = j1939_sk(sk);
+ struct j1939_sk_buff_cb *skb_cb;
+ struct sk_buff *skb;
+ struct net_device *dev;
+ struct j1939_ecu *ecu;
+ int ifindex;
+ int ret;
+
+ if (!(jsk->state | JSK_BOUND))
+ return -ENOTCONN;
+
+ if (msg->msg_name && (msg->msg_namelen <
+ required_size(can_addr.j1939, struct sockaddr_can)))
+ return -EINVAL;
+
+ ifindex = jsk->sk.sk_bound_dev_if;
+ if (msg->msg_name) {
+ struct sockaddr_can *addr = msg->msg_name;
+ if (msg->msg_namelen < required_size(can_addr.j1939, *addr))
+ return -EFAULT;
+ if (addr->can_family != AF_CAN)
+ return -EINVAL;
+ if (ifindex && addr->can_ifindex &&
+ (ifindex != addr->can_ifindex))
+ return -ENONET;
+ if (!ifindex)
+ /* take destination intf when intf not yet set */
+ ifindex = addr->can_ifindex;
+ }
+
+ if (!ifindex)
+ return -EDESTADDRREQ;
+ if (j1939_no_address(&jsk->sk)) {
+ lock_sock(&jsk->sk);
+ ecu = j1939_ecu_find_segment_default_tx(
+ jsk->sk.sk_bound_dev_if,
+ &jsk->addr.src, &jsk->addr.sa);
+ release_sock(&jsk->sk);
+ if (IS_ERR(ecu))
+ return PTR_ERR(ecu);
+ }
+
+ dev = dev_get_by_index(&init_net, ifindex);
+ if (!dev)
+ return -ENXIO;
+
+ skb = sock_alloc_send_skb(sk, size,
+ msg->msg_flags & MSG_DONTWAIT, &ret);
+ if (!skb)
+ goto put_dev;
+
+ ret = memcpy_fromiovec(skb_put(skb, size), msg->msg_iov, size);
+ if (ret < 0)
+ goto free_skb;
+ skb->dev = dev;
+ skb->sk = sk;
+
+ BUILD_BUG_ON(sizeof(skb->cb) < sizeof(*skb_cb));
+
+ skb_cb = (void *) skb->cb;
+ memset(skb_cb, 0, sizeof(*skb_cb));
+ skb_cb->msg_flags = msg->msg_flags;
+ skb_cb->ifindex = ifindex;
+ skb_cb->src.name = jsk->addr.src;
+ skb_cb->dst.name = jsk->addr.dst;
+ skb_cb->pgn = jsk->addr.pgn;
+ skb_cb->priority = j1939_prio(jsk->sk.sk_priority);
+ skb_cb->src.addr = jsk->addr.sa;
+ skb_cb->dst.addr = jsk->addr.da;
+
+ if (msg->msg_name) {
+ struct sockaddr_can *addr = msg->msg_name;
+ if (addr->can_addr.j1939.name) {
+ ecu = j1939_ecu_find_by_name(addr->can_addr.j1939.name,
+ ifindex);
+ if (!ecu)
+ return -EADDRNOTAVAIL;
+ skb_cb->dst.name = ecu->name;
+ skb_cb->dst.addr = ecu->sa;
+ put_j1939_ecu(ecu);
+ } else {
+ skb_cb->dst.name = 0;
+ skb_cb->dst.addr = addr->can_addr.j1939.addr;
+ }
+ if (pgn_is_valid(addr->can_addr.j1939.pgn))
+ skb_cb->pgn = addr->can_addr.j1939.pgn;
+ }
+
+ if (skb_cb->msg_flags & J1939_MSG_SYNC) {
+ if (skb_cb->msg_flags & MSG_DONTWAIT) {
+ ret = j1939_sock_pending_add_first(&jsk->sk);
+ if (ret > 0)
+ ret = -EAGAIN;
+ } else {
+ ret = wait_event_interruptible(jsk->waitq,
+ j1939_sock_pending_add_first(&jsk->sk));
+ }
+ if (ret < 0)
+ goto free_skb;
+ } else {
+ j1939_sock_pending_add(&jsk->sk);
+ }
+
+ ret = j1939_send(skb, j1939_level_sky);
+ if (ret < 0)
+ goto decrement_pending;
+
+ dev_put(dev);
+ return size;
+
+decrement_pending:
+ j1939_sock_pending_del(&jsk->sk);
+free_skb:
+ kfree_skb(skb);
+put_dev:
+ dev_put(dev);
+ return ret;
+}
+
+/* PROC */
+static int j1939sk_proc_show(struct seq_file *sqf, void *v)
+{
+ struct j1939_sock *jsk;
+ struct net_device *netdev;
+
+ seq_printf(sqf, "iface\tflags\tlocal\tremote\tpgn\tprio\tpending\n");
+ mutex_lock(&s.lock);
+ list_for_each_entry(jsk, &s.socks, list) {
+ lock_sock(&jsk->sk);
+ netdev = NULL;
+ if (jsk->sk.sk_bound_dev_if)
+ netdev = dev_get_by_index(&init_net,
+ jsk->sk.sk_bound_dev_if);
+ seq_printf(sqf, "%s\t", netdev ? netdev->name : "-");
+ if (netdev)
+ dev_put(netdev);
+ seq_printf(sqf, "%c%c%c%c\t",
+ (jsk->state & JSK_BOUND) ? 'b' : '-',
+ (jsk->state & JSK_CONNECTED) ? 'c' : '-',
+ (jsk->state & PROMISC) ? 'P' : '-',
+ (jsk->state & RECV_OWN) ? 'o' : '-');
+ if (jsk->addr.src)
+ seq_printf(sqf, "%016llx", (long long)jsk->addr.src);
+ else if (j1939_address_is_unicast(jsk->addr.sa))
+ seq_printf(sqf, "%02x", jsk->addr.sa);
+ else
+ seq_printf(sqf, "-");
+ seq_printf(sqf, "\t");
+ if (jsk->addr.dst)
+ seq_printf(sqf, "%016llx", (long long)jsk->addr.dst);
+ else if (j1939_address_is_unicast(jsk->addr.da))
+ seq_printf(sqf, "%02x", jsk->addr.da);
+ else
+ seq_printf(sqf, "-");
+ seq_printf(sqf, "\t%05x", jsk->addr.pgn);
+ seq_printf(sqf, "\t%u", j1939_prio(jsk->sk.sk_priority));
+ seq_printf(sqf, "\t%u", jsk->skb_pending);
+ release_sock(&jsk->sk);
+ seq_printf(sqf, "\n");
+ }
+ mutex_unlock(&s.lock);
+ return 0;
+}
+
+void j1939sk_netdev_event(int ifindex, int error_code)
+{
+ struct j1939_sock *jsk;
+
+ mutex_lock(&s.lock);
+ list_for_each_entry(jsk, &s.socks, list) {
+ if (jsk->sk.sk_bound_dev_if != ifindex)
+ continue;
+ jsk->sk.sk_err = error_code;
+ if (!sock_flag(&jsk->sk, SOCK_DEAD))
+ jsk->sk.sk_error_report(&jsk->sk);
+ /* do not remove filters here */
+ }
+ mutex_unlock(&s.lock);
+}
+
+static const struct proto_ops j1939_ops = {
+ .family = PF_CAN,
+ .release = j1939sk_release,
+ .bind = j1939sk_bind,
+ .connect = j1939sk_connect,
+ .socketpair = sock_no_socketpair,
+ .accept = sock_no_accept,
+ .getname = j1939sk_getname,
+ .poll = datagram_poll,
+ .ioctl = can_ioctl,
+ .listen = sock_no_listen,
+ .shutdown = sock_no_shutdown,
+ .setsockopt = j1939sk_setsockopt,
+ .getsockopt = j1939sk_getsockopt,
+ .sendmsg = j1939sk_sendmsg,
+ .recvmsg = j1939sk_recvmsg,
+ .mmap = sock_no_mmap,
+ .sendpage = sock_no_sendpage,
+};
+
+static struct proto j1939_proto __read_mostly = {
+ .name = "CAN_J1939",
+ .owner = THIS_MODULE,
+ .obj_size = sizeof(struct j1939_sock),
+ .init = j1939sk_init,
+};
+
+static const struct can_proto j1939_can_proto = {
+ .type = SOCK_DGRAM,
+ .protocol = CAN_J1939,
+ .ops = &j1939_ops,
+ .prot = &j1939_proto,
+
+ .rtnl_link_ops = &j1939_rtnl_af_ops,
+ .rtnl_new_addr = j1939rtnl_new_addr,
+ .rtnl_del_addr = j1939rtnl_del_addr,
+ .rtnl_dump_addr = j1939rtnl_dump_addr,
+};
+
+__init int j1939sk_module_init(void)
+{
+ int ret;
+
+ INIT_LIST_HEAD(&s.socks);
+ mutex_init(&s.lock);
+
+ ret = can_proto_register(&j1939_can_proto);
+ if (ret < 0)
+ pr_err("can: registration of j1939 protocol failed\n");
+ else
+ j1939_proc_add("sock", j1939sk_proc_show, NULL);
+ return ret;
+}
+
+void j1939sk_module_exit(void)
+{
+ j1939_proc_remove("sock");
+ can_proto_unregister(&j1939_can_proto);
+}
+
+MODULE_ALIAS("can-proto-" __stringify(CAN_J1939));
+
diff --git a/net/can/j1939/transport.c b/net/can/j1939/transport.c
new file mode 100644
index 0000000..9f723c6
--- /dev/null
+++ b/net/can/j1939/transport.c
@@ -0,0 +1,1449 @@
+/*
+ * Copyright (c) 2010-2011 EIA Electronics
+ *
+ * Authors:
+ * Kurt Van Dijck <kurt.van.dijck-/BeEPy95v10@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the version 2 of the GNU General Public License
+ * as published by the Free Software Foundation
+ */
+
+#include <linux/skbuff.h>
+#include <linux/hrtimer.h>
+#include <linux/version.h>
+#include <linux/if_arp.h>
+#include <linux/wait.h>
+#include "j1939-priv.h"
+
+#define REGULAR 0
+#define EXTENDED 1
+
+#define etp_pgn_ctl 0xc800
+#define etp_pgn_dat 0xc700
+#define tp_pgn_ctl 0xec00
+#define tp_pgn_dat 0xeb00
+
+#define tp_cmd_bam 0x20
+#define tp_cmd_rts 0x10
+#define tp_cmd_cts 0x11
+#define tp_cmd_eof 0x13
+#define tp_cmd_abort 0xff
+
+#define etp_cmd_rts 0x14
+#define etp_cmd_cts 0x15
+#define etp_cmd_dpo 0x16
+#define etp_cmd_eof 0x17
+#define etp_cmd_abort 0xff
+
+#define ABORT_BUSY 1
+#define ABORT_RESOURCE 2
+#define ABORT_TIMEOUT 3
+#define ABORT_GENERIC 4
+#define ABORT_FAULT 5
+
+#define MAX_TP_PACKET_SIZE (7*255)
+#define MAX_ETP_PACKET_SIZE (7*0xffffff)
+
+static int block = 255;
+static int max_packet_size = 1024*100;
+static int retry_ms = 20;
+
+struct session {
+ struct list_head list;
+ atomic_t refs;
+ spinlock_t lock;
+
+ struct j1939_sk_buff_cb *cb; /*
+ * ifindex, src, dst, pgn define the session block
+ * the are _never_ modified after insertion in the list
+ * this decreases locking problems a _lot_
+ */
+ struct sk_buff *skb;
+
+ /*
+ * all tx related stuff (last_txcmd, pkt.tx)
+ * is protected (modified only) with the txtask tasklet
+ * 'total' & 'block' are never changed,
+ * last_cmd, last & block are protected by ->lock
+ * this means that the tx may run after cts is received that should
+ * have stopped tx, but this time discrepancy is never avoided anyhow
+ */
+ uint8_t last_cmd, last_txcmd;
+ uint8_t transmission;
+ uint8_t extd;
+ struct {
+ /*
+ * these do not require 16 bit, they should fit in uint8_t
+ * but putting in int makes it easier to deal with
+ */
+ unsigned int total, done, last, tx;
+ unsigned int block; /* for TP */
+ unsigned int dpo; /* for ETP */
+ } pkt;
+ struct hrtimer txtimer, rxtimer;
+ /* tasklets for execution of tx/rx timer hander in softirq */
+ struct tasklet_struct txtask, rxtask;
+};
+
+static struct j1939tp {
+ spinlock_t lock;
+ struct list_head sessionq;
+ struct list_head extsessionq;
+ struct {
+ struct list_head sessionq;
+ spinlock_t lock;
+ struct work_struct work;
+ } del;
+ wait_queue_head_t wait;
+ struct notifier_block notifier;
+} s;
+
+static struct session *j1939session_new(struct sk_buff *skb);
+static struct session *j1939session_fresh_new(int size,
+ struct j1939_sk_buff_cb *rel_cb, pgn_t pgn);
+
+static inline void fix_cb(struct j1939_sk_buff_cb *cb)
+{
+ cb->msg_flags &= ~J1939_MSG_RESERVED;
+}
+
+static inline struct list_head *sessionq(int extd)
+{
+ return extd ? &s.extsessionq : &s.sessionq;
+}
+
+static inline void j1939session_destroy(struct session *session)
+{
+ if (session->skb)
+ kfree_skb(session->skb);
+ hrtimer_cancel(&session->rxtimer);
+ hrtimer_cancel(&session->txtimer);
+ tasklet_disable(&session->rxtask);
+ tasklet_disable(&session->txtask);
+ kfree(session);
+}
+
+/* clean up work queue */
+static void j1939tp_del_work(struct work_struct *work)
+{
+ struct session *session;
+ int cnt = 0;
+
+ do {
+ session = NULL;
+ spin_lock_bh(&s.del.lock);
+ if (list_empty(&s.del.sessionq)) {
+ spin_unlock_bh(&s.del.lock);
+ break;
+ }
+ session = list_first_entry(&s.del.sessionq,
+ struct session, list);
+ list_del_init(&session->list);
+ spin_unlock_bh(&s.del.lock);
+ j1939session_destroy(session);
+ ++cnt;
+ } while (1);
+}
+/* reference counter */
+static inline void get_session(struct session *session)
+{
+ atomic_inc(&session->refs);
+}
+
+static void put_session(struct session *session)
+{
+ BUG_ON(!session);
+ if (atomic_add_return(-1, &session->refs) >= 0)
+ /* not the last one */
+ return;
+ /* it should have been removed from any list long time ago */
+ BUG_ON(!list_empty(&session->list));
+
+ hrtimer_try_to_cancel(&session->rxtimer);
+ hrtimer_try_to_cancel(&session->txtimer);
+ tasklet_disable_nosync(&session->rxtask);
+ tasklet_disable_nosync(&session->txtask);
+
+ if (in_interrupt()) {
+ spin_lock_bh(&s.del.lock);
+ list_add_tail(&session->list, &s.del.sessionq);
+ spin_unlock_bh(&s.del.lock);
+ schedule_work(&s.del.work);
+ } else {
+ /* destroy session right here */
+ j1939session_destroy(session);
+ }
+}
+
+/* transport status locking */
+static inline void session_lock(struct session *session)
+{
+ get_session(session); /* safety measure */
+ spin_lock_bh(&session->lock);
+}
+
+static inline void session_unlock(struct session *session)
+{
+ spin_unlock_bh(&session->lock);
+ put_session(session);
+}
+
+static inline void sessionlist_lock(void)
+{
+ spin_lock_bh(&s.lock);
+}
+
+static inline void sessionlist_unlock(void)
+{
+ spin_unlock_bh(&s.lock);
+}
+
+/*
+ * see if we are receiver
+ * returns 0 for broadcasts, although we will receive them
+ */
+static inline int j1939tp_im_receiver(const struct j1939_sk_buff_cb *cb)
+{
+ return (cb->dst.flags & ECUFLAG_LOCAL) ? 1 : 0;
+}
+
+/* see if we are sender */
+static inline int j1939tp_im_transmitter(const struct j1939_sk_buff_cb *cb)
+{
+ return (cb->src.flags & ECUFLAG_LOCAL) ? 1 : 0;
+}
+
+/* see if we are involved as either receiver or transmitter */
+/* reverse = -1 means : any direction */
+static int j1939tp_im_involved(const struct j1939_sk_buff_cb *cb, int reverse)
+{
+ if (reverse < 0) {
+ return ((cb->src.flags | cb->dst.flags) & ECUFLAG_LOCAL)
+ ? 1 : 0;
+ } else if (reverse) {
+ return j1939tp_im_receiver(cb);
+ } else {
+ return j1939tp_im_transmitter(cb);
+ }
+}
+
+/* extract pgn from flow-ctl message */
+static inline pgn_t j1939xtp_ctl_to_pgn(const uint8_t *dat)
+{
+ pgn_t pgn;
+
+ pgn = (dat[7] << 16) | (dat[6] << 8) | (dat[5] << 0);
+ if (pgn_is_pdu1(pgn))
+ pgn &= 0xffff00;
+ return pgn;
+}
+
+static inline unsigned int j1939tp_ctl_to_size(const uint8_t *dat)
+{
+ return (dat[2] << 8) + (dat[1] << 0);
+}
+static inline unsigned int j1939etp_ctl_to_packet(const uint8_t *dat)
+{
+ return (dat[4] << 16) | (dat[3] << 8) | (dat[2] << 0);
+}
+static inline unsigned int j1939etp_ctl_to_size(const uint8_t *dat)
+{
+ return (dat[4] << 24) | (dat[3] << 16) |
+ (dat[2] << 8) | (dat[1] << 0);
+}
+
+/*
+ * find existing session:
+ * reverse: swap cb's src & dst
+ * there is no problem with matching broadcasts, since
+ * broadcasts (no dst, no da) would never call this
+ * with reverse==1
+ */
+static int j1939tp_match(const struct j1939_sk_buff_cb *a,
+ const struct j1939_sk_buff_cb *b, int reverse)
+{
+ if (a->ifindex != b->ifindex)
+ return 0;
+ if (!reverse) {
+ if (a->src.name) {
+ if (a->src.name != b->src.name)
+ return 0;
+ } else if (a->src.addr != b->src.addr)
+ return 0;
+ if (a->dst.name) {
+ if (a->dst.name != b->dst.name)
+ return 0;
+ } else if (a->dst.addr != b->dst.addr)
+ return 0;
+ } else {
+ if (a->src.name) {
+ if (a->src.name != b->dst.name)
+ return 0;
+ } else if (a->src.addr != b->dst.addr)
+ return 0;
+ if (a->dst.name) {
+ if (a->dst.name != b->src.name)
+ return 0;
+ } else if (a->dst.addr != b->src.addr)
+ return 0;
+ }
+ return 1;
+}
+
+static struct session *_j1939tp_find(struct list_head *root,
+ const struct j1939_sk_buff_cb *cb, int reverse)
+{
+ struct session *session;
+
+ list_for_each_entry(session, root, list) {
+ get_session(session);
+ if (j1939tp_match(session->cb, cb, reverse))
+ return session;
+ put_session(session);
+ }
+ return NULL;
+}
+
+static struct session *j1939tp_find(struct list_head *root,
+ const struct j1939_sk_buff_cb *cb, int reverse)
+{
+ struct session *session;
+ sessionlist_lock();
+ session = _j1939tp_find(root, cb, reverse);
+ sessionlist_unlock();
+ return session;
+}
+
+static void j1939_skbcb_swap(struct j1939_sk_buff_cb *cb)
+{
+ name_t name;
+ uint8_t addr;
+ int flags;
+
+ name = cb->dst.name;
+ cb->dst.name = cb->src.name;
+ cb->src.name = name;
+
+ addr = cb->dst.addr;
+ cb->dst.addr = cb->src.addr;
+ cb->src.addr = addr;
+
+ flags = cb->dst.flags;
+ cb->dst.flags = cb->src.flags;
+ cb->src.flags = flags;
+}
+/* TP transmit packet functions */
+static int j1939tp_tx_dat(struct session *related,
+ const uint8_t *dat, int len)
+{
+ int ret;
+ struct sk_buff *skb;
+ struct j1939_sk_buff_cb *skb_cb;
+ uint8_t *skdat;
+
+ skb = dev_alloc_skb(8);
+ if (unlikely(!skb)) {
+ pr_alert("%s: out of memory?\n", __func__);
+ return -ENOMEM;
+ }
+ skb->protocol = related->skb->protocol;
+ skb->pkt_type = related->skb->pkt_type;
+ skb->ip_summed = related->skb->ip_summed;
+ skb->sk = related->skb->sk;
+
+ skb_cb = (void *)skb->cb;
+ *skb_cb = *(related->cb);
+ fix_cb(skb_cb);
+ /* fix pgn */
+ skb_cb->pgn = related->extd ? etp_pgn_dat : tp_pgn_dat;
+
+ skdat = skb_put(skb, len);
+ memcpy(skdat, dat, len);
+ ret = j1939_send(skb, j1939_level_transport);
+ if (ret < 0)
+ kfree_skb(skb);
+ return ret;
+}
+
+static int j1939xtp_do_tx_ctl(struct sk_buff *related, int extd,
+ int swap_src_dst, pgn_t pgn, const uint8_t dat[5])
+{
+ int ret;
+ struct sk_buff *skb;
+ struct j1939_sk_buff_cb *skb_cb, *rel_cb;
+ uint8_t *skdat;
+
+ rel_cb = (void *)related->cb;
+ if (!j1939tp_im_involved(rel_cb, swap_src_dst))
+ return 0;
+
+ skb = dev_alloc_skb(8);
+ if (unlikely(!skb)) {
+ pr_alert("%s: out of memory?\n", __func__);
+ return -ENOMEM;
+ }
+ skb->protocol = related->protocol;
+ skb->pkt_type = related->pkt_type;
+ skb->ip_summed = related->ip_summed;
+ skb->sk = related->sk;
+
+ skb_cb = (void *)skb->cb;
+ *skb_cb = *rel_cb;
+ fix_cb(skb_cb);
+ if (swap_src_dst)
+ j1939_skbcb_swap(skb_cb);
+ skb_cb->pgn = extd ? etp_pgn_ctl : tp_pgn_ctl;
+
+ skdat = skb_put(skb, 8);
+ memcpy(skdat, dat, 5);
+ skdat[7] = (pgn >> 16) & 0xff;
+ skdat[6] = (pgn >> 8) & 0xff;
+ skdat[5] = (pgn >> 0) & 0xff;
+
+ ret = j1939_send(skb, j1939_level_transport);
+ if (ret)
+ kfree_skb(skb);
+ return ret;
+}
+
+static inline int j1939tp_tx_ctl(struct session *session,
+ int swap_src_dst, const uint8_t dat[8])
+{
+ return j1939xtp_do_tx_ctl(session->skb, session->extd, swap_src_dst,
+ session->cb->pgn, dat);
+}
+
+static int j1939xtp_tx_abort(struct sk_buff *related, int extd,
+ int swap_src_dst, int err, pgn_t pgn)
+{
+ struct j1939_sk_buff_cb *cb = (void *)related->cb;
+ uint8_t dat[5];
+
+ if (!j1939tp_im_involved(cb, swap_src_dst))
+ return 0;
+
+ memset(dat, 0xff, sizeof(dat));
+ dat[0] = tp_cmd_abort;
+ if (!extd)
+ dat[1] = err ?: ABORT_GENERIC;
+ return j1939xtp_do_tx_ctl(related, extd, swap_src_dst, pgn, dat);
+}
+
+/* timer & scheduler functions */
+static inline void j1939session_schedule_txnow(struct session *session)
+{
+ tasklet_schedule(&session->txtask);
+}
+static enum hrtimer_restart j1939tp_txtimer(struct hrtimer *hrtimer)
+{
+ struct session *session =
+ container_of(hrtimer, struct session, txtimer);
+ j1939session_schedule_txnow(session);
+ return HRTIMER_NORESTART;
+}
+static inline void j1939tp_schedule_txtimer(struct session *session, int msec)
+{
+ hrtimer_start(&session->txtimer,
+ ktime_set(msec / 1000, (msec % 1000)*1000000UL),
+ HRTIMER_MODE_REL);
+}
+static inline void j1939tp_set_rxtimeout(struct session *session, int msec)
+{
+ hrtimer_start(&session->rxtimer,
+ ktime_set(msec / 1000, (msec % 1000)*1000000UL),
+ HRTIMER_MODE_REL);
+}
+
+/*
+ * session completion functions
+ */
+/*
+ * j1939session_drop
+ * removes a session from open session list
+ */
+static inline void j1939session_drop(struct session *session)
+{
+ sessionlist_lock();
+ list_del_init(&session->list);
+ sessionlist_unlock();
+
+ if (session->transmission) {
+ if (session->skb && session->skb->sk)
+ j1939_sock_pending_del(session->skb->sk);
+ wake_up_all(&s.wait);
+ }
+ put_session(session);
+}
+
+static inline void j1939session_completed(struct session *session)
+{
+ j1939_recv(session->skb, j1939_level_transport);
+ j1939session_drop(session);
+}
+
+static void j1939session_cancel(struct session *session, int err)
+{
+ if ((err >= 0) && j1939tp_im_involved(session->cb, -1)) {
+ if (!j1939cb_is_broadcast(session->cb)) {
+ /* do not send aborts on incoming broadcasts */
+ j1939xtp_tx_abort(session->skb, session->extd,
+ !j1939tp_im_transmitter(session->cb),
+ err, session->cb->pgn);
+ }
+ }
+ j1939session_drop(session);
+}
+
+static enum hrtimer_restart j1939tp_rxtimer(struct hrtimer *hrtimer)
+{
+ struct session *session =
+ container_of(hrtimer, struct session, rxtimer);
+ tasklet_schedule(&session->rxtask);
+ return HRTIMER_NORESTART;
+}
+
+static void j1939tp_rxtask(unsigned long val)
+{
+ struct session *session = (void *)val;
+
+ get_session(session);
+ pr_alert("%s: timeout on %i\n", __func__, session->cb->ifindex);
+ j1939session_cancel(session, ABORT_TIMEOUT);
+ put_session(session);
+}
+
+/*
+ * receive packet functions
+ */
+static void _j1939xtp_rx_bad_message(struct sk_buff *skb, int extd)
+{
+ struct j1939_sk_buff_cb *cb = (void *)skb->cb;
+ struct session *session;
+ pgn_t pgn;
+
+ pgn = j1939xtp_ctl_to_pgn(skb->data);
+ session = j1939tp_find(sessionq(extd), cb, 0);
+ if (session /*&& (session->cb->pgn == pgn)*/) {
+ /* do not allow TP control messages on 2 pgn's */
+ j1939session_cancel(session, ABORT_FAULT);
+ put_session(session); /* ~j1939tp_find */
+ return;
+ }
+ j1939xtp_tx_abort(skb, extd, 0, ABORT_FAULT, pgn);
+ if (!session)
+ return;
+ put_session(session); /* ~j1939tp_find */
+}
+
+/* abort packets may come in 2 directions */
+static void j1939xtp_rx_bad_message(struct sk_buff *skb, int extd)
+{
+ struct j1939_sk_buff_cb *cb = (void *)skb->cb;
+
+ pr_info("%s, pgn %05x\n", __func__, j1939xtp_ctl_to_pgn(skb->data));
+ _j1939xtp_rx_bad_message(skb, extd);
+ j1939_skbcb_swap(cb);
+ _j1939xtp_rx_bad_message(skb, extd);
+ /* restore skb */
+ j1939_skbcb_swap(cb);
+ return;
+}
+
+static void _j1939xtp_rx_abort(struct sk_buff *skb, int extd)
+{
+ struct j1939_sk_buff_cb *cb = (void *)skb->cb;
+ struct session *session;
+ pgn_t pgn;
+
+ pgn = j1939xtp_ctl_to_pgn(skb->data);
+ session = j1939tp_find(sessionq(extd), cb, 0);
+ if (!session)
+ return;
+ if (session->transmission && !session->last_txcmd) {
+ /*
+ * empty block:
+ * do not drop session when a transmit session did not
+ * start yet
+ */
+ } else if (session->cb->pgn == pgn)
+ j1939session_drop(session);
+ /* another PGN had a bad message */
+ /*
+ * TODO: maybe cancel current connection
+ * as another pgn was communicated
+ */
+ put_session(session); /* ~j1939tp_find */
+}
+/* abort packets may come in 2 directions */
+static inline void j1939xtp_rx_abort(struct sk_buff *skb, int extd)
+{
+ struct j1939_sk_buff_cb *cb = (void *)skb->cb;
+
+ pr_info("%s %i, %05x\n", __func__, cb->ifindex,
+ j1939xtp_ctl_to_pgn(skb->data));
+ _j1939xtp_rx_abort(skb, extd);
+ j1939_skbcb_swap(cb);
+ _j1939xtp_rx_abort(skb, extd);
+ /* restore skb */
+ j1939_skbcb_swap(cb);
+ return;
+}
+
+static void j1939xtp_rx_eof(struct sk_buff *skb, int extd)
+{
+ struct j1939_sk_buff_cb *cb = (void *)skb->cb;
+ struct session *session;
+ pgn_t pgn;
+
+ /* end of tx cycle */
+ pgn = j1939xtp_ctl_to_pgn(skb->data);
+ session = j1939tp_find(sessionq(extd), cb, 1);
+ if (!session)
+ /*
+ * strange, we had EOF on closed connection
+ * do nothing, as EOF closes the connection anyway
+ */
+ return;
+
+ if (session->cb->pgn != pgn) {
+ j1939xtp_tx_abort(skb, extd, 1, ABORT_BUSY, pgn);
+ j1939session_cancel(session, ABORT_BUSY);
+ } else {
+ /* transmitted without problems */
+ j1939session_completed(session);
+ }
+ put_session(session); /* ~j1939tp_find */
+}
+
+static void j1939xtp_rx_cts(struct sk_buff *skb, int extd)
+{
+ struct j1939_sk_buff_cb *cb = (void *)skb->cb;
+ struct session *session;
+ pgn_t pgn;
+ unsigned int pkt;
+ const uint8_t *dat;
+
+ dat = skb->data;
+ pgn = j1939xtp_ctl_to_pgn(skb->data);
+ session = j1939tp_find(sessionq(extd), cb, 1);
+ if (!session) {
+ /* 'CTS shall be ignored' */
+ return;
+ }
+ if (session->cb->pgn != pgn) {
+ /* what to do? */
+ j1939xtp_tx_abort(skb, extd, 1, ABORT_BUSY, pgn);
+ j1939session_cancel(session, ABORT_BUSY);
+ put_session(session); /* ~j1939tp_find */
+ return;
+ }
+ session_lock(session);
+ pkt = extd ? j1939etp_ctl_to_packet(dat) : dat[2];
+ if (!dat[0])
+ hrtimer_cancel(&session->txtimer);
+ else if (!pkt)
+ goto bad_fmt;
+ else if (dat[1] > session->pkt.block /* 0xff for etp */)
+ goto bad_fmt;
+ else {
+ /* set packet counters only when not CTS(0) */
+ session->pkt.done = pkt - 1;
+ session->pkt.last = session->pkt.done + dat[1];
+ if (session->pkt.last > session->pkt.total)
+ /* safety measure */
+ session->pkt.last = session->pkt.total;
+ /* TODO: do not set tx here, do it in txtask */
+ session->pkt.tx = session->pkt.done;
+ }
+ session->last_cmd = dat[0];
+ session_unlock(session);
+ if (dat[1]) {
+ j1939tp_set_rxtimeout(session, 1250);
+ if (j1939tp_im_transmitter(session->cb))
+ j1939session_schedule_txnow(session);
+ } else {
+ /* CTS(0) */
+ j1939tp_set_rxtimeout(session, 550);
+ }
+ put_session(session); /* ~j1939tp_find */
+ return;
+bad_fmt:
+ session_unlock(session);
+ j1939session_cancel(session, ABORT_FAULT);
+ put_session(session); /* ~j1939tp_find */
+}
+
+static void j1939xtp_rx_rts(struct sk_buff *skb, int extd)
+{
+ struct j1939_sk_buff_cb *cb = (void *)skb->cb;
+ struct session *session;
+ int len;
+ const uint8_t *dat;
+ pgn_t pgn;
+
+ dat = skb->data;
+ pgn = j1939xtp_ctl_to_pgn(dat);
+
+ if ((tp_cmd_rts == dat[0]) && j1939cb_is_broadcast(cb)) {
+ pr_alert("%s: rts without destination (%i %02x)\n", __func__,
+ cb->ifindex, cb->src.addr);
+ return;
+ }
+ /*
+ * TODO: abort RTS when a similar
+ * TP is pending in the other direction
+ */
+ session = j1939tp_find(sessionq(extd), cb, 0);
+ if (session && !j1939tp_im_transmitter(cb)) {
+ /* RTS on pending connection */
+ j1939session_cancel(session, ABORT_BUSY);
+ if ((pgn != session->cb->pgn) && (tp_cmd_bam != dat[0]))
+ j1939xtp_tx_abort(skb, extd, 1, ABORT_BUSY, pgn);
+ put_session(session); /* ~j1939tp_find */
+ return;
+ } else if (!session && j1939tp_im_transmitter(cb)) {
+ pr_alert("%s: I should tx (%i %02x %02x)\n", __func__,
+ cb->ifindex, cb->src.addr, cb->dst.addr);
+ return;
+ }
+ if (session && (0 != session->last_cmd)) {
+ /* we received a second rts on the same connection */
+ pr_alert("%s: connection exists (%i %02x %02x)\n", __func__,
+ cb->ifindex, cb->src.addr, cb->dst.addr);
+ j1939session_cancel(session, ABORT_BUSY);
+ put_session(session); /* ~j1939tp_find */
+ return;
+ }
+ if (session) {
+ /*
+ * make sure 'sa' & 'da' are correct !
+ * They may be 'not filled in yet' for sending
+ * skb's, since they did not pass the Address Claim ever.
+ */
+ session->cb->src.addr = cb->src.addr;
+ session->cb->dst.addr = cb->dst.addr;
+ } else {
+ int abort = 0;
+ if (extd) {
+ len = j1939etp_ctl_to_size(dat);
+ if (len > (max_packet_size ?: MAX_ETP_PACKET_SIZE))
+ abort = ABORT_RESOURCE;
+ else if (len <= MAX_TP_PACKET_SIZE)
+ abort = ABORT_FAULT;
+ } else {
+ len = j1939tp_ctl_to_size(dat);
+ if (len > MAX_TP_PACKET_SIZE)
+ abort = ABORT_FAULT;
+ else if (max_packet_size && (len > max_packet_size))
+ abort = ABORT_RESOURCE;
+ }
+ if (abort) {
+ j1939xtp_tx_abort(skb, extd, 1, abort, pgn);
+ return;
+ }
+ session = j1939session_fresh_new(len, cb, pgn);
+ if (!session) {
+ j1939xtp_tx_abort(skb, extd, 1, ABORT_RESOURCE, pgn);
+ return;
+ }
+ session->extd = extd;
+ /* initialize the control buffer: plain copy */
+ session->pkt.total = (len+6)/7;
+ session->pkt.block = 0xff;
+ if (!extd) {
+ if (dat[3] != session->pkt.total)
+ pr_alert("%s: strange total,"
+ " %u != %u\n", __func__,
+ session->pkt.total, dat[3]);
+ session->pkt.total = dat[3];
+ session->pkt.block = dat[4];
+ }
+ session->pkt.done = session->pkt.tx = 0;
+ get_session(session); /* equivalent to j1939tp_find() */
+ sessionlist_lock();
+ list_add_tail(&session->list, sessionq(extd));
+ sessionlist_unlock();
+ }
+ session->last_cmd = dat[0];
+
+ j1939tp_set_rxtimeout(session, 1250);
+
+ if (j1939tp_im_receiver(session->cb)) {
+ if (extd || (tp_cmd_bam != dat[0]))
+ j1939session_schedule_txnow(session);
+ }
+ /*
+ * as soon as it's inserted, things can go fast
+ * protect against a long delay
+ * between spin_unlock & next statement
+ * so, only release here, at the end
+ */
+ put_session(session); /* ~j1939tp_find */
+ return;
+}
+
+static void j1939xtp_rx_dpo(struct sk_buff *skb, int extd)
+{
+ struct j1939_sk_buff_cb *cb = (void *)skb->cb;
+ struct session *session;
+ pgn_t pgn;
+ const uint8_t *dat = skb->data;
+
+ pgn = j1939xtp_ctl_to_pgn(dat);
+ session = j1939tp_find(sessionq(extd), cb, 0);
+ if (!session) {
+ pr_info("%s: %s\n", __func__, "no connection found");
+ return;
+ }
+
+ if (session->cb->pgn != pgn) {
+ pr_info("%s: different pgn\n", __func__);
+ j1939xtp_tx_abort(skb, 1, 1, ABORT_BUSY, pgn);
+ j1939session_cancel(session, ABORT_BUSY);
+ put_session(session); /* ~j1939tp_find */
+ return;
+ }
+ /* transmitted without problems */
+ session->pkt.dpo = j1939etp_ctl_to_packet(skb->data);
+ session->last_cmd = dat[0];
+ j1939tp_set_rxtimeout(session, 750);
+ put_session(session); /* ~j1939tp_find */
+}
+
+static void j1939xtp_rx_dat(struct sk_buff *skb, int extd)
+{
+ struct j1939_sk_buff_cb *cb = (void *)skb->cb;
+ struct session *session;
+ const uint8_t *dat;
+ uint8_t *tpdat;
+ int offset;
+ int nbytes;
+ int final;
+ int do_cts_eof;
+ int packet;
+
+ session = j1939tp_find(sessionq(extd), cb, 0);
+ if (!session) {
+ pr_info("%s:%s\n", __func__, "no connection found");
+ return;
+ }
+ dat = skb->data;
+ if (skb->len <= 1)
+ /* makes no sense */
+ goto strange_packet_unlocked;
+
+ session_lock(session);
+
+ switch (session->last_cmd) {
+ case 0xff:
+ break;
+ case etp_cmd_dpo:
+ if (extd)
+ break;
+ case tp_cmd_bam:
+ case tp_cmd_cts:
+ if (!extd)
+ break;
+ default:
+ pr_info("%s: last %02x\n", __func__,
+ session->last_cmd);
+ goto strange_packet;
+ }
+
+ packet = (dat[0]-1+session->pkt.dpo);
+ offset = packet * 7;
+ if ((packet > session->pkt.total) ||
+ (session->pkt.done+1) > session->pkt.total) {
+ pr_info("%s: should have been completed\n", __func__);
+ goto strange_packet;
+ }
+ nbytes = session->skb->len - offset;
+ if (nbytes > 7)
+ nbytes = 7;
+ if ((nbytes <= 0) || ((nbytes + 1) > skb->len)) {
+ pr_info("%s: nbytes %i, len %i\n", __func__, nbytes,
+ skb->len);
+ goto strange_packet;
+ }
+ tpdat = session->skb->data;
+ memcpy(&tpdat[offset], &dat[1], nbytes);
+ if (packet == session->pkt.done)
+ ++session->pkt.done;
+
+ if (!extd && j1939cb_is_broadcast(session->cb)) {
+ final = session->pkt.done >= session->pkt.total;
+ do_cts_eof = 0;
+ } else {
+ final = 0; /* never final, an EOF must follow */
+ do_cts_eof = (session->pkt.done >= session->pkt.last);
+ }
+ session_unlock(session);
+ if (final) {
+ j1939session_completed(session);
+ } else if (do_cts_eof) {
+ j1939tp_set_rxtimeout(session, 1250);
+ if (j1939tp_im_receiver(session->cb))
+ j1939session_schedule_txnow(session);
+ } else {
+ j1939tp_set_rxtimeout(session, 250);
+ }
+ session->last_cmd = 0xff;
+ put_session(session); /* ~j1939tp_find */
+ return;
+
+strange_packet:
+ /* unlock session (spinlock) before trying to send */
+ session_unlock(session);
+strange_packet_unlocked:
+ j1939session_cancel(session, ABORT_FAULT);
+ put_session(session); /* ~j1939tp_find */
+}
+
+/*
+ * transmit function
+ */
+static int j1939tp_txnext(struct session *session)
+{
+ uint8_t dat[8];
+ const uint8_t *tpdat;
+ int ret, offset, len, pkt_done, pkt_end;
+ unsigned int pkt;
+
+ memset(dat, 0xff, sizeof(dat));
+ get_session(session); /* do not loose it */
+
+ switch (session->last_cmd) {
+ case 0:
+ if (!j1939tp_im_transmitter(session->cb))
+ break;
+ dat[1] = (session->skb->len >> 0) & 0xff;
+ dat[2] = (session->skb->len >> 8) & 0xff;
+ dat[3] = session->pkt.total;
+ if (session->extd) {
+ dat[0] = etp_cmd_rts;
+ dat[1] = (session->skb->len >> 0) & 0xff;
+ dat[2] = (session->skb->len >> 8) & 0xff;
+ dat[3] = (session->skb->len >> 16) & 0xff;
+ dat[4] = (session->skb->len >> 24) & 0xff;
+ } else if (j1939cb_is_broadcast(session->cb)) {
+ dat[0] = tp_cmd_bam;
+ /* fake cts for broadcast */
+ session->pkt.tx = 0;
+ } else {
+ dat[0] = tp_cmd_rts;
+ dat[4] = dat[3];
+ }
+ if (dat[0] == session->last_txcmd)
+ /* done already */
+ break;
+ ret = j1939tp_tx_ctl(session, 0, dat);
+ if (ret < 0)
+ goto failed;
+ session->last_txcmd = dat[0];
+ /* must lock? */
+ if (tp_cmd_bam == dat[0])
+ j1939tp_schedule_txtimer(session, 50);
+ j1939tp_set_rxtimeout(session, 1250);
+ break;
+ case tp_cmd_rts:
+ case etp_cmd_rts:
+ if (!j1939tp_im_receiver(session->cb))
+ break;
+tx_cts:
+ ret = 0;
+ len = session->pkt.total - session->pkt.done;
+ if (len > 255)
+ len = 255;
+ if (len > session->pkt.block)
+ len = session->pkt.block;
+ if (block && (len > block))
+ len = block;
+
+ if (session->extd) {
+ pkt = session->pkt.done+1;
+ dat[0] = etp_cmd_cts;
+ dat[1] = len;
+ dat[2] = (pkt >> 0) & 0xff;
+ dat[3] = (pkt >> 8) & 0xff;
+ dat[4] = (pkt >> 16) & 0xff;
+ } else {
+ dat[0] = tp_cmd_cts;
+ dat[1] = len;
+ dat[2] = session->pkt.done+1;
+ }
+ if (dat[0] == session->last_txcmd)
+ /* done already */
+ break;
+ ret = j1939tp_tx_ctl(session, 1, dat);
+ if (ret < 0)
+ goto failed;
+ if (len)
+ /* only mark cts done when len is set */
+ session->last_txcmd = dat[0];
+ j1939tp_set_rxtimeout(session, 1250);
+ break;
+ case etp_cmd_cts:
+ if (j1939tp_im_transmitter(session->cb) && session->extd &&
+ (etp_cmd_dpo != session->last_txcmd)) {
+ /* do dpo */
+ dat[0] = etp_cmd_dpo;
+ session->pkt.dpo = session->pkt.done;
+ pkt = session->pkt.dpo;
+ dat[1] = session->pkt.last - session->pkt.done;
+ dat[2] = (pkt >> 0) & 0xff;
+ dat[3] = (pkt >> 8) & 0xff;
+ dat[4] = (pkt >> 16) & 0xff;
+ ret = j1939tp_tx_ctl(session, 0, dat);
+ if (ret < 0)
+ goto failed;
+ session->last_txcmd = dat[0];
+ j1939tp_set_rxtimeout(session, 1250);
+ session->pkt.tx = session->pkt.done;
+ }
+ case tp_cmd_cts:
+ case 0xff: /* did some data */
+ case etp_cmd_dpo:
+ if ((session->extd || !j1939cb_is_broadcast(session->cb)) &&
+ j1939tp_im_receiver(session->cb)) {
+ if (session->pkt.done >= session->pkt.total) {
+ if (session->extd) {
+ dat[0] = etp_cmd_eof;
+ dat[1] = session->skb->len >> 0;
+ dat[2] = session->skb->len >> 8;
+ dat[3] = session->skb->len >> 16;
+ dat[4] = session->skb->len >> 24;
+ } else {
+ dat[0] = tp_cmd_eof;
+ dat[1] = session->skb->len;
+ dat[2] = session->skb->len >> 8;
+ dat[3] = session->pkt.total;
+ }
+ if (dat[0] == session->last_txcmd)
+ /* done already */
+ break;
+ ret = j1939tp_tx_ctl(session, 1, dat);
+ if (ret < 0)
+ goto failed;
+ session->last_txcmd = dat[0];
+ j1939tp_set_rxtimeout(session, 1250);
+ /* wait for the EOF packet to come in */
+ break;
+ } else if (session->pkt.done >= session->pkt.last) {
+ session->last_txcmd = 0;
+ goto tx_cts;
+ }
+ }
+ case tp_cmd_bam:
+ if (!j1939tp_im_transmitter(session->cb))
+ break;
+ tpdat = session->skb->data;
+ ret = 0;
+ pkt_done = 0;
+ pkt_end = (!session->extd && j1939cb_is_broadcast(session->cb))
+ ? session->pkt.total : session->pkt.last;
+
+ while (session->pkt.tx < pkt_end) {
+ dat[0] = session->pkt.tx - session->pkt.dpo+1;
+ offset = session->pkt.tx * 7;
+ len = session->skb->len - offset;
+ if (len > 7)
+ len = 7;
+ memcpy(&dat[1], &tpdat[offset], len);
+ ret = j1939tp_tx_dat(session, dat, len+1);
+ if (ret < 0)
+ break;
+ session->last_txcmd = 0xff;
+ ++pkt_done;
+ ++session->pkt.tx;
+ if (j1939cb_is_broadcast(session->cb)) {
+ if (session->pkt.tx < session->pkt.total)
+ j1939tp_schedule_txtimer(session, 50);
+ break;
+ }
+ }
+ if (pkt_done)
+ j1939tp_set_rxtimeout(session, 250);
+ if (ret)
+ goto failed;
+ break;
+ }
+ put_session(session);
+ return 0;
+failed:
+ put_session(session);
+ return ret;
+}
+
+static void j1939tp_txtask(unsigned long val)
+{
+ struct session *session = (void *)val;
+ int ret;
+
+ get_session(session);
+ ret = j1939tp_txnext(session);
+ if (ret < 0)
+ j1939tp_schedule_txtimer(session, retry_ms);
+ put_session(session);
+}
+
+static inline int j1939tp_tx_initial(struct session *session)
+{
+ int ret;
+
+ get_session(session);
+ ret = j1939tp_txnext(session);
+ /* set nonblocking for further packets */
+ session->cb->msg_flags |= MSG_DONTWAIT;
+ put_session(session);
+ return ret;
+}
+
+/* this call is to be used as probe within wait_event_xxx() */
+static int j1939session_insert(struct session *session)
+{
+ struct session *pending;
+
+ sessionlist_lock();
+ pending = _j1939tp_find(sessionq(session->extd), session->cb, 0);
+ if (pending)
+ /* revert the effect of find() */
+ put_session(pending);
+ else
+ list_add_tail(&session->list, sessionq(session->extd));
+ sessionlist_unlock();
+ return pending ? 0 : 1;
+}
+/*
+ * j1939 main intf
+ */
+int j1939_send_transport(struct sk_buff *skb)
+{
+ struct j1939_sk_buff_cb *cb = (void *)skb->cb;
+ struct session *session;
+ int ret;
+
+ if ((tp_pgn_dat == cb->pgn) || (tp_pgn_ctl == cb->pgn) ||
+ (etp_pgn_dat == cb->pgn) || (etp_pgn_ctl == cb->pgn))
+ /* avoid conflict */
+ return -EDOM;
+ if (skb->len <= 8)
+ return 0;
+ else if (skb->len > (max_packet_size ?: MAX_ETP_PACKET_SIZE))
+ return -EMSGSIZE;
+
+ if (skb->len > MAX_TP_PACKET_SIZE) {
+ if (j1939cb_is_broadcast(cb))
+ return -EDESTADDRREQ;
+ }
+
+ /* prepare new session */
+ session = j1939session_new(skb);
+ if (!session)
+ return -ENOMEM;
+
+ session->extd = (skb->len > MAX_TP_PACKET_SIZE) ? EXTENDED : REGULAR;
+ session->transmission = 1;
+ session->pkt.total = (skb->len + 6)/7;
+ session->pkt.block = session->extd ? 255 :
+ (block ?: session->pkt.total);
+ if (j1939cb_is_broadcast(session->cb))
+ /* set the end-packet for broadcast */
+ session->pkt.last = session->pkt.total;
+
+ /* insert into queue, but avoid collision with pending session */
+ if (session->cb->msg_flags & MSG_DONTWAIT)
+ ret = j1939session_insert(session) ? 0 : -EAGAIN;
+ else
+ ret = wait_event_interruptible(s.wait,
+ j1939session_insert(session));
+ if (ret < 0)
+ goto failed;
+
+ ret = j1939tp_tx_initial(session);
+ if (!ret)
+ /* transmission started */
+ return RESULT_STOP;
+ sessionlist_lock();
+ list_del_init(&session->list);
+ sessionlist_unlock();
+failed:
+ /*
+ * hide the skb from j1939session_drop, as it would
+ * kfree_skb, but our caller will kfree_skb(skb) too.
+ */
+ session->skb = NULL;
+ j1939session_drop(session);
+ return ret;
+}
+
+int j1939_recv_transport(struct sk_buff *skb)
+{
+ struct j1939_sk_buff_cb *cb = (void *)skb->cb;
+ const uint8_t *dat;
+
+ switch (cb->pgn) {
+ case etp_pgn_dat:
+ j1939xtp_rx_dat(skb, EXTENDED);
+ break;
+ case etp_pgn_ctl:
+ if (skb->len < 8) {
+ j1939xtp_rx_bad_message(skb, EXTENDED);
+ break;
+ }
+ dat = skb->data;
+ switch (*dat) {
+ case etp_cmd_rts:
+ j1939xtp_rx_rts(skb, EXTENDED);
+ break;
+ case etp_cmd_cts:
+ j1939xtp_rx_cts(skb, EXTENDED);
+ break;
+ case etp_cmd_dpo:
+ j1939xtp_rx_dpo(skb, EXTENDED);
+ break;
+ case etp_cmd_eof:
+ j1939xtp_rx_eof(skb, EXTENDED);
+ break;
+ case etp_cmd_abort:
+ j1939xtp_rx_abort(skb, EXTENDED);
+ break;
+ default:
+ j1939xtp_rx_bad_message(skb, EXTENDED);
+ break;
+ }
+ break;
+ case tp_pgn_dat:
+ j1939xtp_rx_dat(skb, REGULAR);
+ break;
+ case tp_pgn_ctl:
+ if (skb->len < 8) {
+ j1939xtp_rx_bad_message(skb, REGULAR);
+ break;
+ }
+ dat = skb->data;
+ switch (*dat) {
+ case tp_cmd_bam:
+ case tp_cmd_rts:
+ j1939xtp_rx_rts(skb, REGULAR);
+ break;
+ case tp_cmd_cts:
+ j1939xtp_rx_cts(skb, REGULAR);
+ break;
+ case tp_cmd_eof:
+ j1939xtp_rx_eof(skb, REGULAR);
+ break;
+ case tp_cmd_abort:
+ j1939xtp_rx_abort(skb, REGULAR);
+ break;
+ default:
+ j1939xtp_rx_bad_message(skb, REGULAR);
+ break;
+ }
+ break;
+ default:
+ return 0;
+ }
+ return RESULT_STOP;
+}
+
+static struct session *j1939session_fresh_new(int size,
+ struct j1939_sk_buff_cb *rel_cb, pgn_t pgn)
+{
+ struct sk_buff *skb;
+ struct j1939_sk_buff_cb *cb;
+ struct session *session;
+
+ skb = dev_alloc_skb(size);
+ if (!skb)
+ return NULL;
+ cb = (void *)skb->cb;
+ *cb = *rel_cb;
+ fix_cb(cb);
+ cb->pgn = pgn;
+
+ session = j1939session_new(skb);
+ if (!session) {
+ kfree(skb);
+ return NULL;
+ }
+ /* alloc data area */
+ skb_put(skb, size);
+ return session;
+}
+static struct session *j1939session_new(struct sk_buff *skb)
+{
+ struct session *session;
+
+ session = kzalloc(sizeof(*session), gfp_any());
+ if (!session)
+ return NULL;
+ INIT_LIST_HEAD(&session->list);
+ spin_lock_init(&session->lock);
+ session->skb = skb;
+
+ session->cb = (void *)session->skb->cb;
+ hrtimer_init(&session->txtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ session->txtimer.function = j1939tp_txtimer;
+ hrtimer_init(&session->rxtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ session->rxtimer.function = j1939tp_rxtimer;
+ tasklet_init(&session->txtask, j1939tp_txtask, (unsigned long)session);
+ tasklet_init(&session->rxtask, j1939tp_rxtask, (unsigned long)session);
+ return session;
+}
+
+static int j1939tp_notifier(struct notifier_block *nb,
+ unsigned long msg, void *data)
+{
+ struct net_device *netdev = (struct net_device *)data;
+ struct session *session, *saved;
+
+ if (!net_eq(dev_net(netdev), &init_net))
+ return NOTIFY_DONE;
+
+ if (netdev->type != ARPHRD_CAN)
+ return NOTIFY_DONE;
+
+ if (msg != NETDEV_UNREGISTER)
+ return NOTIFY_DONE;
+
+ sessionlist_lock();
+ list_for_each_entry_safe(session, saved, &s.sessionq, list) {
+ if (session->cb->ifindex != netdev->ifindex)
+ continue;
+ list_del_init(&session->list);
+ put_session(session);
+ }
+ list_for_each_entry_safe(session, saved, &s.extsessionq, list) {
+ if (session->cb->ifindex != netdev->ifindex)
+ continue;
+ list_del_init(&session->list);
+ put_session(session);
+ }
+ sessionlist_unlock();
+ return NOTIFY_DONE;
+}
+
+/* SYSCTL */
+static struct ctl_table_header *j1939tp_table_header;
+
+static int min_block = 1;
+static int max_block = 255;
+static int min_packet = 8;
+static int max_packet = ((2 << 24)-1)*7;
+
+static int min_retry = 5;
+static int max_retry = 5000;
+
+static ctl_table j1939tp_table[] = {
+ {
+ .procname = "transport_cts_nr_of_frames",
+ .data = &block,
+ .maxlen = sizeof(block),
+ .mode = 0644,
+ .proc_handler = &proc_dointvec_minmax,
+ .extra1 = &min_block,
+ .extra2 = &max_block,
+ },
+ {
+ .procname = "transport_max_payload_in_bytes",
+ .data = &max_packet_size,
+ .maxlen = sizeof(max_packet_size),
+ .mode = 0644,
+ .proc_handler = &proc_dointvec_minmax,
+ .extra1 = &min_packet,
+ .extra2 = &max_packet,
+ },
+ {
+ .procname = "transport_tx_retry_ms",
+ .data = &retry_ms,
+ .maxlen = sizeof(retry_ms),
+ .mode = 0644,
+ .proc_handler = &proc_dointvec_minmax,
+ .extra1 = &min_retry,
+ .extra2 = &max_retry,
+ },
+ { },
+};
+
+static struct ctl_path j1939tp_path[] = {
+ { .procname = "net", },
+ { .procname = j1939_procname, },
+ { }
+};
+
+/* PROC */
+static int j1939tp_proc_show_session(struct seq_file *sqf,
+ struct session *session)
+{
+ seq_printf(sqf, "%i", session->cb->ifindex);
+ if (session->cb->src.name)
+ seq_printf(sqf, "\t%016llx", session->cb->src.name);
+ else
+ seq_printf(sqf, "\t%02x", session->cb->src.addr);
+ if (session->cb->dst.name)
+ seq_printf(sqf, "\t%016llx", session->cb->dst.name);
+ else if (j1939_address_is_unicast(session->cb->dst.addr))
+ seq_printf(sqf, "\t%02x", session->cb->dst.addr);
+ else
+ seq_printf(sqf, "\t-");
+ seq_printf(sqf, "\t%05x\t%u/%u\n", session->cb->pgn,
+ session->pkt.done*7, session->skb->len);
+ return 0;
+}
+
+static int j1939tp_proc_show(struct seq_file *sqf, void *v)
+{
+ struct session *session;
+
+ seq_printf(sqf, "iface\tsrc\tdst\tpgn\tdone/total\n");
+ sessionlist_lock();
+ list_for_each_entry(session, &s.sessionq, list)
+ j1939tp_proc_show_session(sqf, session);
+ list_for_each_entry(session, &s.extsessionq, list)
+ j1939tp_proc_show_session(sqf, session);
+ sessionlist_unlock();
+ return 0;
+}
+
+int __init j1939tp_module_init(void)
+{
+ spin_lock_init(&s.lock);
+ INIT_LIST_HEAD(&s.sessionq);
+ INIT_LIST_HEAD(&s.extsessionq);
+ spin_lock_init(&s.del.lock);
+ INIT_LIST_HEAD(&s.del.sessionq);
+ INIT_WORK(&s.del.work, j1939tp_del_work);
+
+ s.notifier.notifier_call = j1939tp_notifier;
+ register_netdevice_notifier(&s.notifier);
+
+ j1939_proc_add("transport", j1939tp_proc_show, NULL);
+ j1939tp_table_header =
+ register_sysctl_paths(j1939tp_path, j1939tp_table);
+ init_waitqueue_head(&s.wait);
+ return 0;
+}
+
+void j1939tp_module_exit(void)
+{
+ struct session *session, *saved;
+
+ wake_up_all(&s.wait);
+
+ unregister_sysctl_table(j1939tp_table_header);
+ unregister_netdevice_notifier(&s.notifier);
+ j1939_proc_remove("transport");
+ sessionlist_lock();
+ list_for_each_entry_safe(session, saved, &s.extsessionq, list) {
+ list_del_init(&session->list);
+ put_session(session);
+ }
+ list_for_each_entry_safe(session, saved, &s.sessionq, list) {
+ list_del_init(&session->list);
+ put_session(session);
+ }
+ sessionlist_unlock();
+ flush_scheduled_work();
+}
+
--
1.7.2.5
^ permalink raw reply related
* [PATCH v4 2/5] can: add rtnetlink support
From: Kurt Van Dijck @ 2011-04-27 8:57 UTC (permalink / raw)
To: socketcan-core-0fE9KPoRgkgATYTw5x5z8w,
netdev-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20110427085330.GA757-ozGf4kBk5synFtIcQ8t7k3L8HoS0Hn3T@public.gmane.org>
This patch adds rtnetlink support for AF_CAN. This support is really
a multiplexer towards the different CAN protocols.
Signed-off-by: Kurt Van Dijck <kurt.van.dijck-/BeEPy95v10@public.gmane.org>
---
include/linux/can/core.h | 26 +++++-
net/can/af_can.c | 240 +++++++++++++++++++++++++++++++++++++++++++---
net/can/bcm.c | 2 +-
net/can/raw.c | 2 +-
4 files changed, 253 insertions(+), 17 deletions(-)
diff --git a/include/linux/can/core.h b/include/linux/can/core.h
index d803a5e..7ffd839 100644
--- a/include/linux/can/core.h
+++ b/include/linux/can/core.h
@@ -18,6 +18,7 @@
#include <linux/can.h>
#include <linux/skbuff.h>
#include <linux/netdevice.h>
+#include <net/rtnetlink.h>
#define CAN_VERSION "20090105"
@@ -40,6 +41,27 @@ struct can_proto {
int protocol;
const struct proto_ops *ops;
struct proto *prot;
+ const struct rtnl_af_ops *rtnl_link_ops;
+ /*
+ * hooks for rtnl hooks
+ * for the *dump* functions, cb->args[0] is reserved
+ * for use by af_can.c, so keep your fingers off.
+ */
+ rtnl_doit_func rtnl_new_addr;
+ rtnl_doit_func rtnl_del_addr;
+ rtnl_dumpit_func rtnl_dump_addr;
+};
+
+/*
+ * this is quite a dirty hack:
+ * reuse the second byte of a rtnetlink msg
+ * to indicate the precise protocol.
+ * The major problem is that is may conflict
+ * with the prefixlen in struct ifaddrmsg.
+ */
+struct rtgencanmsg {
+ unsigned char rtgen_family;
+ unsigned char can_protocol;
};
/*
@@ -53,8 +75,8 @@ struct can_proto {
/* function prototypes for the CAN networklayer core (af_can.c) */
-extern int can_proto_register(struct can_proto *cp);
-extern void can_proto_unregister(struct can_proto *cp);
+extern int can_proto_register(const struct can_proto *cp);
+extern void can_proto_unregister(const struct can_proto *cp);
extern int can_rx_register(struct net_device *dev, canid_t can_id,
canid_t mask,
diff --git a/net/can/af_can.c b/net/can/af_can.c
index a8dcaa4..c7e9cdf 100644
--- a/net/can/af_can.c
+++ b/net/can/af_can.c
@@ -3,6 +3,7 @@
* (used by different CAN protocol modules)
*
* Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * Copyright (C) 2011 Kurt Van Dijck <kurt.van.dijck-/BeEPy95v10@public.gmane.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -69,7 +70,8 @@ static __initdata const char banner[] = KERN_INFO
MODULE_DESCRIPTION("Controller Area Network PF_CAN core");
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("Urs Thuermann <urs.thuermann-l29pVbxQd1IUtdQbppsyvg@public.gmane.org>, "
- "Oliver Hartkopp <oliver.hartkopp-l29pVbxQd1IUtdQbppsyvg@public.gmane.org>");
+ "Oliver Hartkopp <oliver.hartkopp-l29pVbxQd1IUtdQbppsyvg@public.gmane.org>, "
+ "Kurt Van Dijck <kurt.van.dijck-/BeEPy95v10@public.gmane.org>");
MODULE_ALIAS_NETPROTO(PF_CAN);
@@ -84,7 +86,7 @@ static DEFINE_SPINLOCK(can_rcvlists_lock);
static struct kmem_cache *rcv_cache __read_mostly;
/* table of registered CAN protocols */
-static struct can_proto *proto_tab[CAN_NPROTO] __read_mostly;
+static const struct can_proto *proto_tab[CAN_NPROTO] __read_mostly;
static DEFINE_MUTEX(proto_tab_lock);
struct timer_list can_stattimer; /* timer for statistics update */
@@ -115,9 +117,9 @@ static void can_sock_destruct(struct sock *sk)
skb_queue_purge(&sk->sk_receive_queue);
}
-static struct can_proto *can_try_module_get(int protocol)
+static const struct can_proto *can_try_module_get(int protocol)
{
- struct can_proto *cp;
+ const struct can_proto *cp;
rcu_read_lock();
cp = rcu_dereference(proto_tab[protocol]);
@@ -128,18 +130,20 @@ static struct can_proto *can_try_module_get(int protocol)
return cp;
}
+static inline void can_put_proto(const struct can_proto *cp)
+{
+ module_put(cp->prot->owner);
+}
+
static int can_create(struct net *net, struct socket *sock, int protocol,
int kern)
{
struct sock *sk;
- struct can_proto *cp;
+ const struct can_proto *cp;
int err = 0;
sock->state = SS_UNCONNECTED;
- if (protocol < 0 || protocol >= CAN_NPROTO)
- return -EINVAL;
-
if (!net_eq(net, &init_net))
return -EAFNOSUPPORT;
@@ -166,8 +170,8 @@ static int can_create(struct net *net, struct socket *sock, int protocol,
/* check for available protocol and correct usage */
- if (!cp)
- return -EPROTONOSUPPORT;
+ if (IS_ERR(cp))
+ return PTR_ERR(cp);
if (cp->type != sock->type) {
err = -EPROTOTYPE;
@@ -195,7 +199,7 @@ static int can_create(struct net *net, struct socket *sock, int protocol,
}
errout:
- module_put(cp->prot->owner);
+ can_put_proto(cp);
return err;
}
@@ -691,7 +695,7 @@ drop:
* -EBUSY protocol already in use
* -ENOBUF if proto_register() fails
*/
-int can_proto_register(struct can_proto *cp)
+int can_proto_register(const struct can_proto *cp)
{
int proto = cp->protocol;
int err = 0;
@@ -728,7 +732,7 @@ EXPORT_SYMBOL(can_proto_register);
* can_proto_unregister - unregister CAN transport protocol
* @cp: pointer to CAN protocol structure
*/
-void can_proto_unregister(struct can_proto *cp)
+void can_proto_unregister(const struct can_proto *cp)
{
int proto = cp->protocol;
@@ -818,6 +822,206 @@ static struct notifier_block can_netdev_notifier __read_mostly = {
.notifier_call = can_notifier,
};
+/*
+ * RTNETLINK
+ */
+static int can_rtnl_doit(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
+{
+ int ret, protocol;
+ const struct can_proto *cp;
+ rtnl_doit_func fn;
+
+ protocol = ((struct rtgencanmsg *)NLMSG_DATA(nlh))->can_protocol;
+ /* since rtnl_lock is held, dont try to load protocol */
+ cp = can_get_proto(protocol, 0);
+ if (IS_ERR(cp))
+ return PTR_ERR(cp);
+
+ switch (nlh->nlmsg_type) {
+ case RTM_NEWADDR:
+ fn = cp->rtnl_new_addr;
+ break;
+ case RTM_DELADDR:
+ fn = cp->rtnl_del_addr;
+ break;
+ default:
+ fn = 0;
+ break;
+ }
+ if (fn)
+ ret = fn(skb, nlh, arg);
+ else
+ ret = -EPROTONOSUPPORT;
+ can_put_proto(cp);
+ return ret;
+}
+
+static int can_rtnl_dumpit(struct sk_buff *skb, struct netlink_callback *cb,
+ int offset)
+{
+ int ret, j;
+ const struct can_proto *cp;
+ rtnl_dumpit_func fn;
+
+ ret = 0;
+ for (j = cb->args[0]; j < CAN_NPROTO; ++j) {
+ /* save state */
+ cb->args[0] = j;
+ cp = can_get_proto(j, 0);
+ if (IS_ERR(cp))
+ /* we are looping, any error is our own fault */
+ continue;
+ fn = *((rtnl_dumpit_func *)(&((const uint8_t *)cp)[offset]));
+ if (fn)
+ ret = fn(skb, cb);
+ can_put_proto(cp);
+ if (ret < 0)
+ /* suspend this skb */
+ return ret;
+ }
+ return ret;
+}
+
+static int can_rtnl_dump_addr(struct sk_buff *skb, struct netlink_callback *cb)
+{
+ return can_rtnl_dumpit(skb, cb,
+ offsetof(struct can_proto, rtnl_dump_addr));
+}
+
+/*
+ * LINK AF properties
+ */
+static size_t can_get_link_af_size(const struct net_device *dev)
+{
+ int ret, j, total;
+ const struct can_proto *cp;
+
+ if (!net_eq(dev_net(dev), &init_net) || (dev->type != ARPHRD_CAN))
+ return 0;
+
+ total = 0;
+ for (j = 0; j < CAN_NPROTO; ++j) {
+ cp = can_try_mdoule_get(j);
+ if (IS_ERR(cp))
+ /* no worry */
+ continue;
+ ret = 0;
+ if (cp->rtnl_link_ops && cp->rtnl_link_ops->get_link_af_size)
+ ret = cp->rtnl_link_ops->get_link_af_size(dev) +
+ nla_total_size(sizeof(struct nlattr));
+ can_put_proto(cp);
+ if (ret < 0)
+ return ret;
+ total += ret;
+ }
+ return nla_total_size(total);
+}
+
+static int can_fill_link_af(struct sk_buff *skb, const struct net_device *dev)
+{
+ int ret, j, n;
+ struct nlattr *nla;
+ const struct can_proto *cp;
+
+ if (!net_eq(dev_net(dev), &init_net) || (dev->type != ARPHRD_CAN))
+ return -ENODATA;
+
+ n = 0;
+ for (j = 0; j < CAN_NPROTO; ++j) {
+ cp = can_try_module_get(j);
+ if (IS_ERR(cp))
+ /* no worry */
+ continue;
+ if (cp->rtnl_link_ops && cp->rtnl_link_ops->fill_link_af) {
+ nla = nla_nest_start(skb, j);
+ if (!nla)
+ goto nla_put_failure;
+
+ ret = cp->rtnl_link_ops->fill_link_af(skb, dev);
+ /*
+ * Caller may return ENODATA to indicate that there
+ * was no data to be dumped. This is not an error, it
+ * means we should trim the attribute header and
+ * continue.
+ */
+ if (ret == -ENODATA)
+ nla_nest_cancel(skb, nla);
+ else if (ret < 0)
+ goto nla_put_failure;
+ nla_nest_end(skb, nla);
+ ++n;
+ }
+ can_put_proto(cp);
+ }
+ return n ? 0 : -ENODATA;
+
+nla_put_failure:
+ nla_nest_cancel(skb, nla);
+ can_put_proto(cp);
+ return -EMSGSIZE;
+}
+
+static int can_validate_link_af(const struct net_device *dev,
+ const struct nlattr *nla)
+{
+ int ret, rem;
+ const struct can_proto *cp;
+ struct nlattr *prot;
+
+ if (!net_eq(dev_net(dev), &init_net) || (dev->type != ARPHRD_CAN))
+ return -EPROTONOSUPPORT;
+
+ nla_for_each_nested(prot, nla, rem) {
+ cp = can_try_module_get(nla_type(prot));
+ if (IS_ERR(cp))
+ return PTR_ERR(cp);
+ if (!cp->rtnl_link_ops)
+ ret = -EPROTONOSUPPORT;
+ else if (!cp->rtnl_link_ops->validate_link_af)
+ ret = 0;
+ else
+ ret = cp->rtnl_link_ops->validate_link_af(dev, prot);
+ can_put_proto(cp);
+ if (ret < 0)
+ return ret;
+ }
+ return 0;
+}
+
+static int can_set_link_af(struct net_device *dev, const struct nlattr *nla)
+{
+ int ret, rem;
+ const struct can_proto *cp;
+ struct nlattr *prot;
+
+ if (!net_eq(dev_net(dev), &init_net) || (dev->type != ARPHRD_CAN))
+ return -EPROTONOSUPPORT;
+
+ nla_for_each_nested(prot, nla, rem) {
+ cp = can_try_module_get(nla_type(prot));
+ if (IS_ERR(cp))
+ return PTR_ERR(cp);
+ if (!cp->rtnl_link_ops || !cp->rtnl_link_ops->set_link_af)
+ ret = -EPROTONOSUPPORT;
+ else
+ ret = cp->rtnl_link_ops->set_link_af(dev, prot);
+ can_put_proto(cp);
+ if (ret < 0)
+ return ret;
+ }
+ return 0;
+}
+
+static struct rtnl_af_ops can_rtnl_af_ops = {
+ .family = AF_CAN,
+ .fill_link_af = can_fill_link_af,
+ .get_link_af_size = can_get_link_af_size,
+ .validate_link_af = can_validate_link_af,
+ .set_link_af = can_set_link_af,
+};
+
+/* exported init */
+
static __init int can_init(void)
{
printk(banner);
@@ -843,6 +1047,11 @@ static __init int can_init(void)
register_netdevice_notifier(&can_netdev_notifier);
dev_add_pack(&can_packet);
+ rtnl_af_register(&can_rtnl_af_ops);
+ rtnl_register(PF_CAN, RTM_NEWADDR, can_rtnl_doit, NULL);
+ rtnl_register(PF_CAN, RTM_DELADDR, can_rtnl_doit, NULL);
+ rtnl_register(PF_CAN, RTM_GETADDR, NULL, can_rtnl_dump_addr);
+
return 0;
}
@@ -853,6 +1062,11 @@ static __exit void can_exit(void)
if (stats_timer)
del_timer(&can_stattimer);
+ rtnl_unregister(PF_CAN, RTM_NEWADDR);
+ rtnl_unregister(PF_CAN, RTM_DELADDR);
+ rtnl_unregister(PF_CAN, RTM_GETADDR);
+ rtnl_af_unregister(&can_rtnl_af_ops);
+
can_remove_proc();
/* protocol unregister */
diff --git a/net/can/bcm.c b/net/can/bcm.c
index 3dc4d4e..4f65f4f 100644
--- a/net/can/bcm.c
+++ b/net/can/bcm.c
@@ -1601,7 +1601,7 @@ static struct proto bcm_proto __read_mostly = {
.init = bcm_init,
};
-static struct can_proto bcm_can_proto __read_mostly = {
+static const struct can_proto bcm_can_proto = {
.type = SOCK_DGRAM,
.protocol = CAN_BCM,
.ops = &bcm_ops,
diff --git a/net/can/raw.c b/net/can/raw.c
index 6009d5e..60d4282 100644
--- a/net/can/raw.c
+++ b/net/can/raw.c
@@ -774,7 +774,7 @@ static struct proto raw_proto __read_mostly = {
.init = raw_init,
};
-static struct can_proto raw_can_proto __read_mostly = {
+static const struct can_proto raw_can_proto = {
.type = SOCK_RAW,
.protocol = CAN_RAW,
.ops = &raw_ops,
--
1.7.2.5
^ permalink raw reply related
* [PATCH v4 1/5] can: extend sockaddr_can to include j1939 members
From: Kurt Van Dijck @ 2011-04-27 8:55 UTC (permalink / raw)
To: socketcan-core-0fE9KPoRgkgATYTw5x5z8w,
netdev-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20110427085330.GA757-ozGf4kBk5synFtIcQ8t7k3L8HoS0Hn3T@public.gmane.org>
This patch prepares struct sockaddr_can for SAE J1939.
The size of this structure increases. To stay binary compatible,
the required_size macro is introduced for existing CAN protocols.
Signed-off-by: Kurt Van Dijck <kurt.van.dijck-/BeEPy95v10@public.gmane.org>
---
include/linux/can.h | 20 +++++++++++++++++++-
include/linux/can/core.h | 9 +++++++++
net/can/bcm.c | 4 ++--
net/can/raw.c | 4 ++--
4 files changed, 32 insertions(+), 5 deletions(-)
diff --git a/include/linux/can.h b/include/linux/can.h
index d183333..9c2523c 100644
--- a/include/linux/can.h
+++ b/include/linux/can.h
@@ -67,7 +67,8 @@ struct can_frame {
#define CAN_TP20 4 /* VAG Transport Protocol v2.0 */
#define CAN_MCNET 5 /* Bosch MCNet */
#define CAN_ISOTP 6 /* ISO 15765-2 Transport Protocol */
-#define CAN_NPROTO 7
+#define CAN_J1939 7 /* SAE J1939 */
+#define CAN_NPROTO 8
#define SOL_CAN_BASE 100
@@ -84,6 +85,23 @@ struct sockaddr_can {
/* transport protocol class address information (e.g. ISOTP) */
struct { canid_t rx_id, tx_id; } tp;
+ /* J1939 address information */
+ struct {
+ /* 8 byte name when using dynamic addressing */
+ __u64 name;
+ /*
+ * pgn:
+ * 8bit: PS in PDU2 case, else 0
+ * 8bit: PF
+ * 1bit: DP
+ * 1bit: reserved
+ */
+ __u32 pgn;
+
+ /* 1byte address */
+ __u8 addr;
+ } j1939;
+
/* reserved for future CAN protocols address information */
} can_addr;
};
diff --git a/include/linux/can/core.h b/include/linux/can/core.h
index 6f70a6d..d803a5e 100644
--- a/include/linux/can/core.h
+++ b/include/linux/can/core.h
@@ -42,6 +42,15 @@ struct can_proto {
struct proto *prot;
};
+/*
+ * required_size
+ * macro to find the minimum size of a struct
+ * that includes a requested member
+ */
+#define required_size(member, struct_type) \
+ (offsetof(typeof(struct_type), member) + \
+ sizeof(((typeof(struct_type) *)(0))->member))
+
/* function prototypes for the CAN networklayer core (af_can.c) */
extern int can_proto_register(struct can_proto *cp);
diff --git a/net/can/bcm.c b/net/can/bcm.c
index 8a6a05e..3dc4d4e 100644
--- a/net/can/bcm.c
+++ b/net/can/bcm.c
@@ -1256,7 +1256,7 @@ static int bcm_sendmsg(struct kiocb *iocb, struct socket *sock,
struct sockaddr_can *addr =
(struct sockaddr_can *)msg->msg_name;
- if (msg->msg_namelen < sizeof(*addr))
+ if (msg->msg_namelen < required_size(can_ifindex, *addr))
return -EINVAL;
if (addr->can_family != AF_CAN)
@@ -1498,7 +1498,7 @@ static int bcm_connect(struct socket *sock, struct sockaddr *uaddr, int len,
struct sock *sk = sock->sk;
struct bcm_sock *bo = bcm_sk(sk);
- if (len < sizeof(*addr))
+ if (len < required_size(can_ifindex, *addr))
return -EINVAL;
if (bo->bound)
diff --git a/net/can/raw.c b/net/can/raw.c
index 0eb39a7..6009d5e 100644
--- a/net/can/raw.c
+++ b/net/can/raw.c
@@ -355,7 +355,7 @@ static int raw_bind(struct socket *sock, struct sockaddr *uaddr, int len)
int err = 0;
int notify_enetdown = 0;
- if (len < sizeof(*addr))
+ if (len < required_size(can_ifindex, *addr))
return -EINVAL;
lock_sock(sk);
@@ -654,7 +654,7 @@ static int raw_sendmsg(struct kiocb *iocb, struct socket *sock,
struct sockaddr_can *addr =
(struct sockaddr_can *)msg->msg_name;
- if (msg->msg_namelen < sizeof(*addr))
+ if (msg->msg_namelen < required_size(can_ifindex, *addr))
return -EINVAL;
if (addr->can_family != AF_CAN)
--
1.7.2.5
^ permalink raw reply related
* [PATCH v4 0/5] CAN: add SAE J1939 protocol
From: Kurt Van Dijck @ 2011-04-27 8:53 UTC (permalink / raw)
To: socketcan-core-0fE9KPoRgkgATYTw5x5z8w,
netdev-u79uwXL29TY76Z2rM5mHXA
This series adds SAE J1939 support to the current net-next-2.6.
1/5: can: extend sockaddr_can to include j1939 members
2/5: can: add rtnetlink support
3/5: j1939: initial import of SAE J1939
4/5: j1939: add documentation & MAINTAINERS
5/5: iproute2: use CAN & J1939
Differences from v3:
* Address claiming does not need a state machine anymore
* Dropped a per-ECU lock.
* Tasklets fill the gap between hrtimer (hardirq context)
and networking (softirq context), without needing
spin_lock_irqsave() & friends.
* Added example sendto() code in documentation
Differences from v2:
* replace magic constants with macro's
* use a static protocol stack, not a dynamic one.
* remove feature that have limited use and are
difficult to explain.
* renamed some structure types to increase code readability.
* block on transport sessions until CAN frame is queued.
Kind regards,
Kurt Van Dijck
^ permalink raw reply
* Re: [PATCH 00/13] Swap-over-NBD without deadlocking
From: Mel Gorman @ 2011-04-27 8:43 UTC (permalink / raw)
To: Peter Zijlstra; +Cc: Linux-MM, Linux-Netdev, LKML, David Miller, Neil Brown
In-Reply-To: <1303829449.20212.285.camel@twins>
On Tue, Apr 26, 2011 at 04:50:49PM +0200, Peter Zijlstra wrote:
> On Tue, 2011-04-26 at 15:46 +0100, Mel Gorman wrote:
> >
> > I did find that only a few route-cache entries should be required. In
> > the original patches I worked with, there was a reservation for the
> > maximum possible number of route-cache entries. I thought this was
> > overkill and instead reserved 1-per-active-swapfile-backed-by-NFS.
>
> Right, so the thing I was worried about was a route-cache poison attack
> where someone would spam the machine such that it would create a lot of
> route cache entries and might flush the one we needed just as we needed
> it.
>
> Pinning the one entry we need would solve that (if possible).
That is a possibility all right, nice thoughts there. Ok, as I do
not want this series to grow to the point where it is unreviewable,
I'll mark pinning the routing cache entry for a follow-on series.
In this series, the throttling logic should allow a new routing cache
entry to be allocated by kswapd as it's immune to the throttle.
Thanks.
--
Mel Gorman
SUSE Labs
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
^ permalink raw reply
* Re: [PATCH 12/13] mm: Throttle direct reclaimers if PF_MEMALLOC reserves are low and swap is backed by network storage
From: Mel Gorman @ 2011-04-27 8:36 UTC (permalink / raw)
To: NeilBrown; +Cc: Linux-MM, Linux-Netdev, LKML, David Miller, Peter Zijlstra
In-Reply-To: <20110427091811.153ca78b@notabene.brown>
On Wed, Apr 27, 2011 at 09:18:11AM +1000, NeilBrown wrote:
> On Tue, 26 Apr 2011 15:26:24 +0100 Mel Gorman <mgorman@suse.de> wrote:
>
> > On Tue, Apr 26, 2011 at 10:30:59PM +1000, NeilBrown wrote:
> > > On Tue, 26 Apr 2011 08:36:53 +0100 Mel Gorman <mgorman@suse.de> wrote:
> > >
> > >
> > > > +/*
> > > > + * Throttle direct reclaimers if backing storage is backed by the network
> > > > + * and the PFMEMALLOC reserve for the preferred node is getting dangerously
> > > > + * depleted. kswapd will continue to make progress and wake the processes
> > > > + * when the low watermark is reached
> > > > + */
> > > > +static void throttle_direct_reclaim(gfp_t gfp_mask, struct zonelist *zonelist,
> > > > + nodemask_t *nodemask)
> > > > +{
> > > > + struct zone *zone;
> > > > + int high_zoneidx = gfp_zone(gfp_mask);
> > > > + DEFINE_WAIT(wait);
> > > > +
> > > > + /* Check if the pfmemalloc reserves are ok */
> > > > + first_zones_zonelist(zonelist, high_zoneidx, NULL, &zone);
> > > > + prepare_to_wait(&zone->zone_pgdat->pfmemalloc_wait, &wait,
> > > > + TASK_INTERRUPTIBLE);
> > > > + if (pfmemalloc_watermark_ok(zone->zone_pgdat, high_zoneidx))
> > > > + goto out;
> > > > +
> > > > + /* Throttle */
> > > > + do {
> > > > + schedule();
> > > > + finish_wait(&zone->zone_pgdat->pfmemalloc_wait, &wait);
> > > > + prepare_to_wait(&zone->zone_pgdat->pfmemalloc_wait, &wait,
> > > > + TASK_INTERRUPTIBLE);
> > > > + } while (!pfmemalloc_watermark_ok(zone->zone_pgdat, high_zoneidx) &&
> > > > + !fatal_signal_pending(current));
> > > > +
> > > > +out:
> > > > + finish_wait(&zone->zone_pgdat->pfmemalloc_wait, &wait);
> > > > +}
> > >
> > > You are doing an interruptible wait, but only checking for fatal signals.
> > > So if a non-fatal signal arrives, you will busy-wait.
> > >
> > > So I suspect you want TASK_KILLABLE, so just use:
> > >
> > > wait_event_killable(zone->zone_pgdat->pfmemalloc_wait,
> > > pgmemalloc_watermark_ok(zone->zone_pgdata,
> > > high_zoneidx));
> > >
> >
> > Well, if a normal signal arrives, we do not necessarily want the
> > process to enter reclaim. For fatal signals, I allow it to continue
> > because it's not likely to be putting the system under more pressure
> > if it's exiting.
>
> Yep, I understand that and it doesn't seem unreasonable.
>
> However I don't think the code implements that correctly.
>
> If you get a non-fatal signal, schedule will exit immediately (because of the
> TASK_INTERRUPTIBLE setting) and the 'while' clause will succeed because the
> signal is not fatal, so it will loop around and try to schedule again, which
> will again exit immediately - busy loop.
>
Ah, I see. Once again, well spotted.
> >
> > > (You also have an extraneous call to finish_wait)
> > >
> >
> > Which one? I'm not seeing a flow where finish_wait gets called twice
> > without a prepare_to_wait in between.
> >
>
> You don't need to call finish_wait immediately before prepare_to_wait.
>
> It really is best to just use the appropriate 'wait_event*' macro....
>
wait_event_interruptible it is. Thanks
--
Mel Gorman
SUSE Labs
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
^ permalink raw reply
* Question for canutils
From: Tomoya MORINAGA @ 2011-04-27 8:36 UTC (permalink / raw)
To: 'Wolfgang Grandegger'
Cc: socketcan-core, netdev, linux-kernel, toshiharu-linux
Hi
I have 2 questions for canutils.
1) Build issue
I downloaded the latest canutils and libsocketcan.
- canutils-4.0.6
- libsocketcan-0.0.8
Firstly, I installed libsocketcan-0.0.8
Secondly, I tried to install canutils-4.0.6.
But it failed like below.
[root@localhost canutils-4.0.6]# ./configure
...snip...
checking whether lstat correctly handles trailing slash... yes
checking whether stat accepts an empty string... no
checking for socket... yes
checking for strchr... yes
checking for strtoul... yes
checking for pkg-config... /usr/bin/pkg-config
checking pkg-config is at least version 0.9.0... yes
checking for libsocketcan... no
configure: error: *** libsocketcan version above 0.0.8 not found on your system
[root@localhost canutils-4.0.6]#
Do you have any information about the fail?
BTW, Using canutils-3.0.2, I could confirm build becomes success.
----------------------------------------------------------------------------------------------------------------------
2) How to use
Executing candump like following, I see the following message.(Of course, pch_can have already installed)
[root@localhost morinaga]# candump can0
interface = can0, family = 29, type = 3, proto = 1
read: Network is down
[root@localhost morinaga]#
Would you tell me how to active can0 interface.
----------------------------------------------------------------------------------------------------------------------
With Best Regards,
(I will be away from today- 5-May.)
-----------------------------------------
Tomoya MORINAGA
OKI SEMICONDUCTOR CO., LTD.
^ permalink raw reply
* [PATCH 6/6] sctp: sctp: Add ASCONF operation on the single-homed host
From: Michio Honda @ 2011-04-27 8:30 UTC (permalink / raw)
To: netdev; +Cc: Honda Michio, YOSHIFUJI Hideaki, Wei Yongjun
>From 9ede9db0ec4b03d3061a5bfed78328cb5528b908 Mon Sep 17 00:00:00 2001
From: Michio Honda <micchie@sfc.wide.ad.jp>
Date: Tue, 26 Apr 2011 20:19:36 +0900
Subject: [PATCH 6/6] sctp: sctp: Add ASCONF operation on the single-homed host
In this case, the SCTP association transmits an ASCONF packet
including addition of the new IP address and deletion of the old
address. This patch implements this functionality.
In this case, the ASCONF chunk is added to the beginning of the
queue, because the other chunks cannot be transmitted in this state.
Signed-off-by: Michio Honda <micchie@sfc.wide.ad.jp>
Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
---
include/net/sctp/structs.h | 2 +
net/sctp/associola.c | 6 ++++
net/sctp/ipv6.c | 7 +++++
net/sctp/outqueue.c | 13 ++++++++++
net/sctp/protocol.c | 4 ++-
net/sctp/sm_make_chunk.c | 38 ++++++++++++++++++++++++++++++
net/sctp/socket.c | 55 +++++++++++++++++++++++++++++++++++++++----
7 files changed, 118 insertions(+), 7 deletions(-)
diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h
index f7f6add..095f698 100644
--- a/include/net/sctp/structs.h
+++ b/include/net/sctp/structs.h
@@ -1916,6 +1916,8 @@ struct sctp_association {
* after reaching 4294967295.
*/
__u32 addip_serial;
+ union sctp_addr *asconf_addr_del_pending;
+ int src_out_of_asoc_ok;
/* SCTP AUTH: list of the endpoint shared keys. These
* keys are provided out of band by the user applicaton
diff --git a/net/sctp/associola.c b/net/sctp/associola.c
index 1a21c57..10dc059 100644
--- a/net/sctp/associola.c
+++ b/net/sctp/associola.c
@@ -279,6 +279,8 @@ static struct sctp_association *sctp_association_init(struct sctp_association *a
asoc->peer.asconf_capable = 0;
if (sctp_addip_noauth)
asoc->peer.asconf_capable = 1;
+ asoc->asconf_addr_del_pending = NULL;
+ asoc->src_out_of_asoc_ok = 0;
/* Create an input queue. */
sctp_inq_init(&asoc->base.inqueue);
@@ -443,6 +445,10 @@ void sctp_association_free(struct sctp_association *asoc)
asoc->peer.transport_count = 0;
+ /* Free pending address space being deleted */
+ if (asoc->asconf_addr_del_pending != NULL)
+ kfree(asoc->asconf_addr_del_pending);
+
/* Free any cached ASCONF_ACK chunk. */
sctp_assoc_free_asconf_acks(asoc);
diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c
index 233eb3a..b493916 100644
--- a/net/sctp/ipv6.c
+++ b/net/sctp/ipv6.c
@@ -334,6 +334,13 @@ static void sctp_v6_get_saddr(struct sctp_sock *sk,
matchlen = bmatchlen;
}
}
+ if (laddr->state == SCTP_ADDR_NEW && asoc->src_out_of_asoc_ok) {
+ bmatchlen = sctp_v6_addr_match_len(daddr, &laddr->a);
+ if (!baddr || (matchlen < bmatchlen)) {
+ baddr = &laddr->a;
+ matchlen = bmatchlen;
+ }
+ }
}
if (baddr) {
diff --git a/net/sctp/outqueue.c b/net/sctp/outqueue.c
index 1c88c89..edc7532 100644
--- a/net/sctp/outqueue.c
+++ b/net/sctp/outqueue.c
@@ -754,6 +754,16 @@ static int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout)
*/
list_for_each_entry_safe(chunk, tmp, &q->control_chunk_list, list) {
+ /* RFC 5061, 5.3
+ * F1) This means that until such time as the ASCONF
+ * containing the add is acknowledged, the sender MUST
+ * NOT use the new IP address as a source for ANY SCTP
+ * packet except on carrying an ASCONF Chunk.
+ */
+ if (asoc->src_out_of_asoc_ok &&
+ chunk->chunk_hdr->type != SCTP_CID_ASCONF)
+ continue;
+
list_del_init(&chunk->list);
/* Pick the right transport to use. */
@@ -881,6 +891,9 @@ static int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout)
}
}
+ if (q->asoc->src_out_of_asoc_ok)
+ goto sctp_flush_out;
+
/* Is it OK to send data chunks? */
switch (asoc->state) {
case SCTP_STATE_COOKIE_ECHOED:
diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c
index b58a820..d7309927 100644
--- a/net/sctp/protocol.c
+++ b/net/sctp/protocol.c
@@ -510,7 +510,9 @@ static struct dst_entry *sctp_v4_get_dst(struct sctp_association *asoc,
sctp_v4_dst_saddr(&dst_saddr, dst, htons(bp->port));
rcu_read_lock();
list_for_each_entry_rcu(laddr, &bp->address_list, list) {
- if (!laddr->valid || (laddr->state != SCTP_ADDR_SRC))
+ if (!laddr->valid || (laddr->state == SCTP_ADDR_DEL) ||
+ (laddr->state != SCTP_ADDR_SRC &&
+ !asoc->src_out_of_asoc_ok))
continue;
if (sctp_v4_cmp_addr(&dst_saddr, &laddr->a))
goto out_unlock;
diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c
index 3740603..6363c46 100644
--- a/net/sctp/sm_make_chunk.c
+++ b/net/sctp/sm_make_chunk.c
@@ -2768,6 +2768,12 @@ struct sctp_chunk *sctp_make_asconf_update_ip(struct sctp_association *asoc,
int addr_param_len = 0;
int totallen = 0;
int i;
+ sctp_addip_param_t del_param; /* 8 Bytes (Type 0xC002, Len and CrrID) */
+ struct sctp_af *del_af;
+ int del_addr_param_len = 0;
+ int del_paramlen = sizeof(sctp_addip_param_t);
+ union sctp_addr_param del_addr_param; /* (v4) 8 Bytes, (v6) 20 Bytes */
+ int del_pickup = 0;
/* Get total length of all the address parameters. */
addr_buf = addrs;
@@ -2780,6 +2786,17 @@ struct sctp_chunk *sctp_make_asconf_update_ip(struct sctp_association *asoc,
totallen += addr_param_len;
addr_buf += af->sockaddr_len;
+ if (asoc->asconf_addr_del_pending && !del_pickup) {
+ if (!sctp_in_scope(asoc->asconf_addr_del_pending,
+ sctp_scope(addr)))
+ continue;
+ /* reuse the parameter length from the same scope one */
+ totallen += paramlen;
+ totallen += addr_param_len;
+ del_pickup = 1;
+ asoc->src_out_of_asoc_ok = 1;
+ SCTP_DEBUG_PRINTK("mkasconf_update_ip: picked same-scope del_pending addr, totallen for all addresses is %d\n", totallen);
+ }
}
/* Create an asconf chunk with the required length. */
@@ -2802,6 +2819,19 @@ struct sctp_chunk *sctp_make_asconf_update_ip(struct sctp_association *asoc,
addr_buf += af->sockaddr_len;
}
+ if (flags == SCTP_PARAM_ADD_IP && del_pickup) {
+ addr = asoc->asconf_addr_del_pending;
+ del_af = sctp_get_af_specific(addr->v4.sin_family);
+ del_addr_param_len = del_af->to_addr_param(addr,
+ &del_addr_param);
+ del_param.param_hdr.type = SCTP_PARAM_DEL_IP;
+ del_param.param_hdr.length = htons(del_paramlen +
+ del_addr_param_len);
+ del_param.crr_id = i;
+
+ sctp_addto_chunk(retval, del_paramlen, &del_param);
+ sctp_addto_chunk(retval, del_addr_param_len, &del_addr_param);
+ }
return retval;
}
@@ -3224,6 +3254,11 @@ static void sctp_asconf_param_success(struct sctp_association *asoc,
case SCTP_PARAM_DEL_IP:
local_bh_disable();
sctp_del_bind_addr(bp, &addr);
+ if (asoc->asconf_addr_del_pending != NULL &&
+ sctp_cmp_addr_exact(asoc->asconf_addr_del_pending, &addr)) {
+ kfree(asoc->asconf_addr_del_pending);
+ asoc->asconf_addr_del_pending = NULL;
+ }
local_bh_enable();
list_for_each_entry(transport, &asoc->peer.transport_addr_list,
transports) {
@@ -3381,6 +3416,9 @@ int sctp_process_asconf_ack(struct sctp_association *asoc,
asconf_len -= length;
}
+ if (no_err && asoc->src_out_of_asoc_ok)
+ asoc->src_out_of_asoc_ok = 0;
+
/* Free the cached last sent asconf chunk. */
list_del_init(&asconf->transmitted_list);
sctp_chunk_free(asconf);
diff --git a/net/sctp/socket.c b/net/sctp/socket.c
index 78a2f94..7a21945 100644
--- a/net/sctp/socket.c
+++ b/net/sctp/socket.c
@@ -583,10 +583,6 @@ static int sctp_send_asconf_add_ip(struct sock *sk,
goto out;
}
- retval = sctp_send_asconf(asoc, chunk);
- if (retval)
- goto out;
-
/* Add the new addresses to the bind address list with
* use_as_src set to 0.
*/
@@ -599,6 +595,23 @@ static int sctp_send_asconf_add_ip(struct sock *sk,
SCTP_ADDR_NEW, GFP_ATOMIC);
addr_buf += af->sockaddr_len;
}
+ if (asoc->src_out_of_asoc_ok) {
+ struct sctp_transport *trans;
+
+ list_for_each_entry(trans,
+ &asoc->peer.transport_addr_list, transports) {
+ /* Clear the source and route cache */
+ dst_release(trans->dst);
+ trans->cwnd = min(4*asoc->pathmtu, max_t(__u32,
+ 2*asoc->pathmtu, 4380));
+ trans->ssthresh = asoc->peer.i.a_rwnd;
+ trans->rto = asoc->rto_initial;
+ trans->rtt = trans->srtt = trans->rttvar = 0;
+ sctp_transport_route(trans, NULL,
+ sctp_sk(asoc->base.sk));
+ }
+ }
+ retval = sctp_send_asconf(asoc, chunk);
}
out:
@@ -715,7 +728,9 @@ static int sctp_send_asconf_del_ip(struct sock *sk,
struct sctp_sockaddr_entry *saddr;
int i;
int retval = 0;
+ int stored = 0;
+ chunk = NULL;
if (!sctp_addip_enable)
return retval;
@@ -766,8 +781,32 @@ static int sctp_send_asconf_del_ip(struct sock *sk,
bp = &asoc->base.bind_addr;
laddr = sctp_find_unmatch_addr(bp, (union sctp_addr *)addrs,
addrcnt, sp);
- if (!laddr)
- continue;
+ if ((laddr == NULL) && (addrcnt == 1)) {
+ if (asoc->asconf_addr_del_pending)
+ continue;
+ asoc->asconf_addr_del_pending =
+ kzalloc(sizeof(union sctp_addr), GFP_ATOMIC);
+ asoc->asconf_addr_del_pending->sa.sa_family =
+ addrs->sa_family;
+ asoc->asconf_addr_del_pending->v4.sin_port =
+ htons(bp->port);
+ if (addrs->sa_family == AF_INET) {
+ struct sockaddr_in *sin;
+
+ sin = (struct sockaddr_in *)addrs;
+ asoc->asconf_addr_del_pending->v4.sin_addr.s_addr = sin->sin_addr.s_addr;
+ } else if (addrs->sa_family == AF_INET6) {
+ struct sockaddr_in6 *sin6;
+
+ sin6 = (struct sockaddr_in6 *)addrs;
+ ipv6_addr_copy(&asoc->asconf_addr_del_pending->v6.sin6_addr, &sin6->sin6_addr);
+ }
+ SCTP_DEBUG_PRINTK_IPADDR("send_asconf_del_ip: keep the last address asoc: %p ",
+ " at %p\n", asoc, asoc->asconf_addr_del_pending,
+ asoc->asconf_addr_del_pending);
+ stored = 1;
+ goto skip_mkasconf;
+ }
/* We do not need RCU protection throughout this loop
* because this is done under a socket lock from the
@@ -780,6 +819,7 @@ static int sctp_send_asconf_del_ip(struct sock *sk,
goto out;
}
+skip_mkasconf:
/* Reset use_as_src flag for the addresses in the bind address
* list that are to be deleted.
*/
@@ -805,6 +845,9 @@ static int sctp_send_asconf_del_ip(struct sock *sk,
sctp_sk(asoc->base.sk));
}
+ if (stored)
+ /* We don't need to transmit ASCONF */
+ continue;
retval = sctp_send_asconf(asoc, chunk);
}
out:
--
1.7.3.2
^ permalink raw reply related
* [PATCH 5/6] sctp: Add socket option operation for Auto-ASCONF.
From: Michio Honda @ 2011-04-27 8:30 UTC (permalink / raw)
To: netdev; +Cc: Honda Michio, YOSHIFUJI Hideaki, Wei Yongjun
>From cabf61b74e65a4b2dcd65a2ad7259c3957f277da Mon Sep 17 00:00:00 2001
From: Michio Honda <micchie@sfc.wide.ad.jp>
Date: Tue, 26 Apr 2011 20:16:31 +0900
Subject: [PATCH 5/6] sctp: Add socket option operation for Auto-ASCONF.
This patch allows the application to operate Auto-ASCONF on/off
behavior via setsockopt() and getsockopt().
Signed-off-by: Michio Honda <micchie@sfc.wide.ad.jp>
Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
---
include/net/sctp/user.h | 1 +
net/sctp/socket.c | 68 +++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 69 insertions(+), 0 deletions(-)
diff --git a/include/net/sctp/user.h b/include/net/sctp/user.h
index 32fd512..0842ef0 100644
--- a/include/net/sctp/user.h
+++ b/include/net/sctp/user.h
@@ -92,6 +92,7 @@ typedef __s32 sctp_assoc_t;
#define SCTP_LOCAL_AUTH_CHUNKS 27 /* Read only */
#define SCTP_GET_ASSOC_NUMBER 28 /* Read only */
#define SCTP_GET_ASSOC_ID_LIST 29 /* Read only */
+#define SCTP_AUTO_ASCONF 30
/* Internal Socket Options. Some of the sctp library functions are
* implemented using these socket options.
diff --git a/net/sctp/socket.c b/net/sctp/socket.c
index 68c618a..78a2f94 100644
--- a/net/sctp/socket.c
+++ b/net/sctp/socket.c
@@ -3351,6 +3351,46 @@ static int sctp_setsockopt_del_key(struct sock *sk,
}
+/*
+ * 8.1.23 SCTP_AUTO_ASCONF
+ *
+ * This option will enable or disable the use of the automatic generation of
+ * ASCONF chunks to add and delete addresses to an existing association. Note
+ * that this option has two caveats namely: a) it only affects sockets that
+ * are bound to all addresses available to the SCTP stack, and b) the system
+ * administrator may have an overriding control that turns the ASCONF feature
+ * off no matter what setting the socket option may have.
+ * This option expects an integer boolean flag, where a non-zero value turns on
+ * the option, and a zero value turns off the option.
+ * Note. In this implementation, socket operation overrides default parameter
+ * being set by sysctl as well as FreeBSD implementation
+ */
+static int sctp_setsockopt_auto_asconf(struct sock *sk, char __user *optval,
+ unsigned int optlen)
+{
+ int val;
+ struct sctp_sock *sp = sctp_sk(sk);
+
+ if (optlen < sizeof(int))
+ return -EINVAL;
+ if (get_user(val, (int __user *)optval))
+ return -EFAULT;
+ if (!sctp_is_ep_boundall(sk) && val)
+ return -EINVAL;
+ if ((val && sp->do_auto_asconf) || (!val && !sp->do_auto_asconf))
+ return 0;
+
+ if (val == 0 && sp->do_auto_asconf) {
+ list_del(&sp->auto_asconf_list);
+ sp->do_auto_asconf = 0;
+ } else if (val && !sp->do_auto_asconf) {
+ list_add_tail(&sp->auto_asconf_list,
+ &sctp_auto_asconf_splist);
+ sp->do_auto_asconf = 1;
+ }
+ return 0;
+}
+
/* API 6.2 setsockopt(), getsockopt()
*
@@ -3498,6 +3538,9 @@ SCTP_STATIC int sctp_setsockopt(struct sock *sk, int level, int optname,
case SCTP_AUTH_DELETE_KEY:
retval = sctp_setsockopt_del_key(sk, optval, optlen);
break;
+ case SCTP_AUTO_ASCONF:
+ retval = sctp_setsockopt_auto_asconf(sk, optval, optlen);
+ break;
default:
retval = -ENOPROTOOPT;
break;
@@ -5305,6 +5348,28 @@ static int sctp_getsockopt_assoc_number(struct sock *sk, int len,
}
/*
+ * 8.1.23 SCTP_AUTO_ASCONF
+ * See the corresponding setsockopt entry as description
+ */
+static int sctp_getsockopt_auto_asconf(struct sock *sk, int len,
+ char __user *optval, int __user *optlen)
+{
+ int val = 0;
+
+ if (len < sizeof(int))
+ return -EINVAL;
+
+ len = sizeof(int);
+ if (sctp_sk(sk)->do_auto_asconf && sctp_is_ep_boundall(sk))
+ val = 1;
+ if (put_user(len, optlen))
+ return -EFAULT;
+ if (copy_to_user(optval, &val, len))
+ return -EFAULT;
+ return 0;
+}
+
+/*
* 8.2.6. Get the Current Identifiers of Associations
* (SCTP_GET_ASSOC_ID_LIST)
*
@@ -5488,6 +5553,9 @@ SCTP_STATIC int sctp_getsockopt(struct sock *sk, int level, int optname,
case SCTP_GET_ASSOC_ID_LIST:
retval = sctp_getsockopt_assoc_ids(sk, len, optval, optlen);
break;
+ case SCTP_AUTO_ASCONF:
+ retval = sctp_getsockopt_auto_asconf(sk, len, optval, optlen);
+ break;
default:
retval = -ENOPROTOOPT;
break;
--
1.7.3.2
^ permalink raw reply related
* [PATCH 4/6] sctp: Add sysctl support for Auto-ASCONF.
From: Michio Honda @ 2011-04-27 8:29 UTC (permalink / raw)
To: netdev; +Cc: Honda Michio, YOSHIFUJI Hideaki, Wei Yongjun
>From a62a7f231909709938addc706d29323c2e5ca838 Mon Sep 17 00:00:00 2001
From: Michio Honda <micchie@sfc.wide.ad.jp>
Date: Tue, 26 Apr 2011 17:36:05 +0900
Subject: [PATCH 4/6] sctp: Add sysctl support for Auto-ASCONF.
This patch allows the system administrator to change default Auto-ASCONF on/off behavior via an sysctl value.
Signed-off-by: Michio Honda <micchie@sfc.wide.ad.jp>
Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
---
net/sctp/sysctl.c | 7 +++++++
1 files changed, 7 insertions(+), 0 deletions(-)
diff --git a/net/sctp/sysctl.c b/net/sctp/sysctl.c
index 50cb57f..6b39529 100644
--- a/net/sctp/sysctl.c
+++ b/net/sctp/sysctl.c
@@ -183,6 +183,13 @@ static ctl_table sctp_table[] = {
.proc_handler = proc_dointvec,
},
{
+ .procname = "default_auto_asconf",
+ .data = &sctp_default_auto_asconf,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec,
+ },
+ {
.procname = "prsctp_enable",
.data = &sctp_prsctp_enable,
.maxlen = sizeof(int),
--
1.7.3.2
^ permalink raw reply related
* [PATCH 3/6] sctp: Add Auto-ASCONF support (core).
From: Michio Honda @ 2011-04-27 8:29 UTC (permalink / raw)
To: netdev; +Cc: Honda Michio, YOSHIFUJI Hideaki, Wei Yongjun
>From b82ac29d045015c05831a39debb6e0f5bd894bd1 Mon Sep 17 00:00:00 2001
From: Michio Honda <micchie@sfc.wide.ad.jp>
Date: Tue, 26 Apr 2011 19:32:51 +0900
Subject: [PATCH 3/6] sctp: Add Auto-ASCONF support (core).
SCTP reconfigure the IP addresses in the association by using
ASCONF chunks as mentioned in RFC5061. For example, we can
start to use the newly configured IP address in the existing
association. This patch implements automatic ASCONF operation
in the SCTP stack with address events in the host computer,
which is called auto_asconf.
Signed-off-by: Michio Honda <micchie@sfc.wide.ad.jp>
Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
---
include/net/sctp/sctp.h | 2 +
include/net/sctp/structs.h | 15 +++++
net/sctp/bind_addr.c | 15 +++++
net/sctp/ipv6.c | 2 +
net/sctp/protocol.c | 147 ++++++++++++++++++++++++++++++++++++++++++++
net/sctp/socket.c | 33 +++++++++-
6 files changed, 211 insertions(+), 3 deletions(-)
diff --git a/include/net/sctp/sctp.h b/include/net/sctp/sctp.h
index e606f04..54a101c 100644
--- a/include/net/sctp/sctp.h
+++ b/include/net/sctp/sctp.h
@@ -121,6 +121,7 @@ extern int sctp_copy_local_addr_list(struct sctp_bind_addr *,
int flags);
extern struct sctp_pf *sctp_get_pf_specific(sa_family_t family);
extern int sctp_register_pf(struct sctp_pf *, sa_family_t);
+extern void sctp_addr_wq_mgmt(struct sctp_sockaddr_entry *, int);
/*
* sctp/socket.c
@@ -135,6 +136,7 @@ void sctp_sock_rfree(struct sk_buff *skb);
void sctp_copy_sock(struct sock *newsk, struct sock *sk,
struct sctp_association *asoc);
extern struct percpu_counter sctp_sockets_allocated;
+extern int sctp_asconf_mgmt(struct sctp_sock *, struct sctp_sockaddr_entry *);
/*
* sctp/primitive.c
diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h
index 5c9bada..f7f6add 100644
--- a/include/net/sctp/structs.h
+++ b/include/net/sctp/structs.h
@@ -205,6 +205,11 @@ extern struct sctp_globals {
* It is a list of sctp_sockaddr_entry.
*/
struct list_head local_addr_list;
+ int default_auto_asconf;
+ struct list_head addr_waitq;
+ struct timer_list addr_wq_timer;
+ struct list_head auto_asconf_splist;
+ spinlock_t addr_wq_lock;
/* Lock that protects the local_addr_list writers */
spinlock_t addr_list_lock;
@@ -264,6 +269,11 @@ extern struct sctp_globals {
#define sctp_port_hashtable (sctp_globals.port_hashtable)
#define sctp_local_addr_list (sctp_globals.local_addr_list)
#define sctp_local_addr_lock (sctp_globals.addr_list_lock)
+#define sctp_auto_asconf_splist (sctp_globals.auto_asconf_splist)
+#define sctp_addr_waitq (sctp_globals.addr_waitq)
+#define sctp_addr_wq_timer (sctp_globals.addr_wq_timer)
+#define sctp_addr_wq_lock (sctp_globals.addr_wq_lock)
+#define sctp_default_auto_asconf (sctp_globals.default_auto_asconf)
#define sctp_scope_policy (sctp_globals.ipv4_scope_policy)
#define sctp_addip_enable (sctp_globals.addip_enable)
#define sctp_addip_noauth (sctp_globals.addip_noauth_enable)
@@ -341,6 +351,8 @@ struct sctp_sock {
atomic_t pd_mode;
/* Receive to here while partial delivery is in effect. */
struct sk_buff_head pd_lobby;
+ struct list_head auto_asconf_list;
+ int do_auto_asconf;
};
static inline struct sctp_sock *sctp_sk(const struct sock *sk)
@@ -796,6 +808,8 @@ struct sctp_sockaddr_entry {
__u8 valid;
};
+#define SCTP_ADDRESS_TICK_DELAY 500
+
typedef struct sctp_chunk *(sctp_packet_phandler_t)(struct sctp_association *);
/* This structure holds lists of chunks as we are assembling for
@@ -1239,6 +1253,7 @@ sctp_scope_t sctp_scope(const union sctp_addr *);
int sctp_in_scope(const union sctp_addr *addr, const sctp_scope_t scope);
int sctp_is_any(struct sock *sk, const union sctp_addr *addr);
int sctp_addr_is_valid(const union sctp_addr *addr);
+int sctp_is_ep_boundall(struct sock *sk);
/* What type of endpoint? */
diff --git a/net/sctp/bind_addr.c b/net/sctp/bind_addr.c
index faf71d1..df2eba0 100644
--- a/net/sctp/bind_addr.c
+++ b/net/sctp/bind_addr.c
@@ -536,6 +536,21 @@ int sctp_in_scope(const union sctp_addr *addr, sctp_scope_t scope)
return 0;
}
+int sctp_is_ep_boundall(struct sock *sk)
+{
+ struct sctp_bind_addr *bp;
+ struct sctp_sockaddr_entry *addr;
+
+ bp = &sctp_sk(sk)->ep->base.bind_addr;
+ if (sctp_list_single_entry(&bp->address_list)) {
+ addr = list_entry(bp->address_list.next,
+ struct sctp_sockaddr_entry, list);
+ if (sctp_is_any(sk, &addr->a))
+ return 1;
+ }
+ return 0;
+}
+
/********************************************************************
* 3rd Level Abstractions
********************************************************************/
diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c
index 321f175..233eb3a 100644
--- a/net/sctp/ipv6.c
+++ b/net/sctp/ipv6.c
@@ -105,6 +105,7 @@ static int sctp_inet6addr_event(struct notifier_block *this, unsigned long ev,
addr->valid = 1;
spin_lock_bh(&sctp_local_addr_lock);
list_add_tail_rcu(&addr->list, &sctp_local_addr_list);
+ sctp_addr_wq_mgmt(addr, SCTP_ADDR_NEW);
spin_unlock_bh(&sctp_local_addr_lock);
}
break;
@@ -115,6 +116,7 @@ static int sctp_inet6addr_event(struct notifier_block *this, unsigned long ev,
if (addr->a.sa.sa_family == AF_INET6 &&
ipv6_addr_equal(&addr->a.v6.sin6_addr,
&ifa->addr)) {
+ sctp_addr_wq_mgmt(addr, SCTP_ADDR_DEL);
found = 1;
addr->valid = 0;
list_del_rcu(&addr->list);
diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c
index d5bf91d..b58a820 100644
--- a/net/sctp/protocol.c
+++ b/net/sctp/protocol.c
@@ -636,6 +636,142 @@ static void sctp_v4_ecn_capable(struct sock *sk)
INET_ECN_xmit(sk);
}
+void sctp_addr_wq_timeout_handler(unsigned long arg)
+{
+ struct sctp_sockaddr_entry *addrw, *temp;
+ struct sctp_sock *sp;
+
+ spin_lock_bh(&sctp_addr_wq_lock);
+
+ list_for_each_entry_safe(addrw, temp, &sctp_addr_waitq, list) {
+ SCTP_DEBUG_PRINTK_IPADDR("sctp_addrwq_timo_handler: the first ent in wq %p is ",
+ " for cmd %d at entry %p\n", &sctp_addr_waitq, &addrw->a, addrw->state,
+ addrw);
+
+ /* Now we send an ASCONF for each association */
+ /* Note. we currently don't handle link local IPv6 addressees */
+ if (addrw->a.sa.sa_family == AF_INET6) {
+ struct in6_addr *in6;
+
+ if (ipv6_addr_type(&addrw->a.v6.sin6_addr) &
+ IPV6_ADDR_LINKLOCAL)
+ goto free_next;
+
+ in6 = (struct in6_addr *)&addrw->a.v6.sin6_addr;
+ if (ipv6_chk_addr(&init_net, in6, NULL, 0) == 0 &&
+ addrw->state == SCTP_ADDR_NEW) {
+ unsigned long timeo_val;
+
+ SCTP_DEBUG_PRINTK("sctp_timo_handler: this is on DAD, trying %d sec later\n",
+ SCTP_ADDRESS_TICK_DELAY);
+ timeo_val = jiffies;
+ timeo_val += msecs_to_jiffies(SCTP_ADDRESS_TICK_DELAY);
+ mod_timer(&sctp_addr_wq_timer, timeo_val);
+ break;
+ }
+ }
+
+ list_for_each_entry(sp, &sctp_auto_asconf_splist, auto_asconf_list) {
+ struct sock *sk;
+
+ sk = sctp_opt2sk(sp);
+ /* ignore bound-specific endpoints */
+ if (!sctp_is_ep_boundall(sk))
+ continue;
+ sctp_bh_lock_sock(sk);
+ if (sctp_asconf_mgmt(sp, addrw) < 0)
+ SCTP_DEBUG_PRINTK("sctp_addrwq_timo_handler: sctp_asconf_mgmt failed\n");
+ sctp_bh_unlock_sock(sk);
+ }
+free_next:
+ list_del(&addrw->list);
+ kfree(addrw);
+ }
+ spin_unlock_bh(&sctp_addr_wq_lock);
+}
+
+static void sctp_free_addr_wq(void)
+{
+ struct sctp_sockaddr_entry *addrw;
+ struct sctp_sockaddr_entry *temp;
+
+ spin_lock_bh(&sctp_addr_wq_lock);
+ del_timer(&sctp_addr_wq_timer);
+ list_for_each_entry_safe(addrw, temp, &sctp_addr_waitq, list) {
+ list_del(&addrw->list);
+ kfree(addrw);
+ }
+ spin_unlock_bh(&sctp_addr_wq_lock);
+}
+
+/* lookup the entry for the same address in the addr_waitq
+ * sctp_addr_wq MUST be locked
+ */
+static struct sctp_sockaddr_entry *sctp_addr_wq_lookup(struct sctp_sockaddr_entry *addr)
+{
+ struct sctp_sockaddr_entry *addrw;
+
+ list_for_each_entry(addrw, &sctp_addr_waitq, list) {
+ if (addrw->a.sa.sa_family != addr->a.sa.sa_family)
+ continue;
+ if (addrw->a.sa.sa_family == AF_INET) {
+ if (addrw->a.v4.sin_addr.s_addr ==
+ addr->a.v4.sin_addr.s_addr)
+ return addrw;
+ } else if (addrw->a.sa.sa_family == AF_INET6) {
+ if (ipv6_addr_equal(&addrw->a.v6.sin6_addr,
+ &addr->a.v6.sin6_addr))
+ return addrw;
+ }
+ }
+ return NULL;
+}
+
+void sctp_addr_wq_mgmt(struct sctp_sockaddr_entry *addr, int cmd)
+{
+ struct sctp_sockaddr_entry *addrw;
+ unsigned long timeo_val;
+
+ /* first, we check if an opposite message already exist in the queue.
+ * If we found such message, it is removed.
+ * This operation is a bit stupid, but the DHCP client attaches the
+ * new address after a couple of addition and deletion of that address
+ */
+
+ spin_lock_bh(&sctp_addr_wq_lock);
+ /* Offsets existing events in addr_wq */
+ addrw = sctp_addr_wq_lookup(addr);
+ if (addrw) {
+ if (addrw->state != cmd) {
+ SCTP_DEBUG_PRINTK_IPADDR("sctp_addr_wq_mgmt offsets existing entry for %d ",
+ " in wq %p\n", addrw->state, &addrw->a,
+ &sctp_addr_waitq);
+ list_del(&addrw->list);
+ kfree(addrw);
+ }
+ spin_unlock_bh(&sctp_addr_wq_lock);
+ return;
+ }
+
+ /* OK, we have to add the new address to the wait queue */
+ addrw = kmemdup(addr, sizeof(struct sctp_sockaddr_entry), GFP_ATOMIC);
+ if (addrw == NULL) {
+ spin_unlock_bh(&sctp_addr_wq_lock);
+ return;
+ }
+ addrw->state = cmd;
+ list_add_tail(&addrw->list, &sctp_addr_waitq);
+ SCTP_DEBUG_PRINTK_IPADDR("sctp_addr_wq_mgmt add new entry for cmd:%d ",
+ " in wq %p\n", addrw->state, &addrw->a, &sctp_addr_waitq);
+
+ if (!timer_pending(&sctp_addr_wq_timer)) {
+ timeo_val = jiffies;
+ timeo_val += msecs_to_jiffies(SCTP_ADDRESS_TICK_DELAY);
+ mod_timer(&sctp_addr_wq_timer, timeo_val);
+ }
+ spin_unlock_bh(&sctp_addr_wq_lock);
+}
+
/* Event handler for inet address addition/deletion events.
* The sctp_local_addr_list needs to be protocted by a spin lock since
* multiple notifiers (say IPv4 and IPv6) may be running at the same
@@ -663,6 +799,7 @@ static int sctp_inetaddr_event(struct notifier_block *this, unsigned long ev,
addr->valid = 1;
spin_lock_bh(&sctp_local_addr_lock);
list_add_tail_rcu(&addr->list, &sctp_local_addr_list);
+ sctp_addr_wq_mgmt(addr, SCTP_ADDR_NEW);
spin_unlock_bh(&sctp_local_addr_lock);
}
break;
@@ -673,6 +810,7 @@ static int sctp_inetaddr_event(struct notifier_block *this, unsigned long ev,
if (addr->a.sa.sa_family == AF_INET &&
addr->a.v4.sin_addr.s_addr ==
ifa->ifa_local) {
+ sctp_addr_wq_mgmt(addr, SCTP_ADDR_DEL);
found = 1;
addr->valid = 0;
list_del_rcu(&addr->list);
@@ -1256,6 +1394,7 @@ SCTP_STATIC __init int sctp_init(void)
/* Disable ADDIP by default. */
sctp_addip_enable = 0;
sctp_addip_noauth = 0;
+ sctp_default_auto_asconf = 0;
/* Enable PR-SCTP by default. */
sctp_prsctp_enable = 1;
@@ -1280,6 +1419,13 @@ SCTP_STATIC __init int sctp_init(void)
spin_lock_init(&sctp_local_addr_lock);
sctp_get_local_addr_list();
+ /* Initialize the address event list */
+ INIT_LIST_HEAD(&sctp_addr_waitq);
+ INIT_LIST_HEAD(&sctp_auto_asconf_splist);
+ spin_lock_init(&sctp_addr_wq_lock);
+ sctp_addr_wq_timer.expires = 0;
+ setup_timer(&sctp_addr_wq_timer, sctp_addr_wq_timeout_handler, 0);
+
status = sctp_v4_protosw_init();
if (status)
@@ -1351,6 +1497,7 @@ SCTP_STATIC __exit void sctp_exit(void)
/* Unregister with inet6/inet layers. */
sctp_v6_del_protocol();
sctp_v4_del_protocol();
+ sctp_free_addr_wq();
/* Free the control endpoint. */
inet_ctl_sock_destroy(sctp_ctl_sock);
diff --git a/net/sctp/socket.c b/net/sctp/socket.c
index f694ee1..68c618a 100644
--- a/net/sctp/socket.c
+++ b/net/sctp/socket.c
@@ -811,6 +811,22 @@ out:
return retval;
}
+/* set addr events to assocs in the endpoint. ep and addr_wq must be locked */
+int sctp_asconf_mgmt(struct sctp_sock *sp, struct sctp_sockaddr_entry *addrw)
+{
+ struct sock *sk = sctp_opt2sk(sp);
+ union sctp_addr *addr;
+
+ /* It is safe to write port space in caller. */
+ addr = &addrw->a;
+ addr->v4.sin_port = htons(sp->ep->base.bind_addr.port);
+
+ if (addrw->state == SCTP_ADDR_NEW)
+ return sctp_send_asconf_add_ip(sk, (struct sockaddr *)addr, 1);
+ else
+ return sctp_send_asconf_del_ip(sk, (struct sockaddr *)addr, 1);
+}
+
/* Helper for tunneling sctp_bindx() requests through sctp_setsockopt()
*
* API 8.1
@@ -3764,6 +3780,13 @@ SCTP_STATIC int sctp_init_sock(struct sock *sk)
local_bh_disable();
percpu_counter_inc(&sctp_sockets_allocated);
sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1);
+ if (sctp_default_auto_asconf) {
+ list_add_tail(&sp->auto_asconf_list,
+ &sctp_auto_asconf_splist);
+ sp->do_auto_asconf = 1;
+ } else
+ sp->do_auto_asconf = 0;
+ SCTP_DEBUG_PRINTK("sctp_init_sk sk:%p ep:%p\n", sk, ep);
local_bh_enable();
return 0;
@@ -3772,13 +3795,17 @@ SCTP_STATIC int sctp_init_sock(struct sock *sk)
/* Cleanup any SCTP per socket resources. */
SCTP_STATIC void sctp_destroy_sock(struct sock *sk)
{
- struct sctp_endpoint *ep;
+ struct sctp_sock *sp;
SCTP_DEBUG_PRINTK("sctp_destroy_sock(sk: %p)\n", sk);
/* Release our hold on the endpoint. */
- ep = sctp_sk(sk)->ep;
- sctp_endpoint_free(ep);
+ sp = sctp_sk(sk);
+ if (sp->do_auto_asconf) {
+ sp->do_auto_asconf = 0;
+ list_del(&sp->auto_asconf_list);
+ }
+ sctp_endpoint_free(sp->ep);
local_bh_disable();
percpu_counter_dec(&sctp_sockets_allocated);
sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1);
--
1.7.3.2
^ permalink raw reply related
* [PATCH 2/6] sctp: Allow regular C expression in 4th argument for SCTP_DEBUG_PRINTK_IPADDR macro.
From: Michio Honda @ 2011-04-27 8:28 UTC (permalink / raw)
To: netdev; +Cc: Honda Michio, YOSHIFUJI Hideaki, Wei Yongjun
>From e535067d5c26223c7230232ee8e228370db113a6 Mon Sep 17 00:00:00 2001
From: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
Date: Tue, 26 Apr 2011 19:23:24 +0900
Subject: [PATCH 2/6] sctp: Allow regular C expression in 4th argument for SCTP_DEBUG_PRINTK_IPADDR macro.
Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
---
include/net/sctp/sctp.h | 9 +++++----
1 files changed, 5 insertions(+), 4 deletions(-)
diff --git a/include/net/sctp/sctp.h b/include/net/sctp/sctp.h
index 7e8e34c..e606f04 100644
--- a/include/net/sctp/sctp.h
+++ b/include/net/sctp/sctp.h
@@ -286,20 +286,21 @@ do { \
pr_cont(fmt, ##args); \
} while (0)
#define SCTP_DEBUG_PRINTK_IPADDR(fmt_lead, fmt_trail, \
- args_lead, saddr, args_trail...) \
+ args_lead, addr, args_trail...) \
do { \
+ const union sctp_addr *_addr = (addr); \
if (sctp_debug_flag) { \
- if (saddr->sa.sa_family == AF_INET6) { \
+ if (_addr->sa.sa_family == AF_INET6) { \
printk(KERN_DEBUG \
pr_fmt(fmt_lead "%pI6" fmt_trail), \
args_lead, \
- &saddr->v6.sin6_addr, \
+ &_addr->v6.sin6_addr, \
args_trail); \
} else { \
printk(KERN_DEBUG \
pr_fmt(fmt_lead "%pI4" fmt_trail), \
args_lead, \
- &saddr->v4.sin_addr.s_addr, \
+ &_addr->v4.sin_addr.s_addr, \
args_trail); \
} \
} \
--
1.7.3.2
^ permalink raw reply related
* [PATCH 1/6] sctp: Add ADD/DEL ASCONF handling at the receiver.
From: Michio Honda @ 2011-04-27 8:28 UTC (permalink / raw)
To: netdev; +Cc: Honda Michio, YOSHIFUJI Hideaki, Wei Yongjun
>From f658209eb180368b85e2db0178820103113ca1ef Mon Sep 17 00:00:00 2001
From: Michio Honda <micchie@sfc.wide.ad.jp>
Date: Tue, 26 Apr 2011 17:37:02 +0900
Subject: [PATCH 1/6] sctp: Add ADD/DEL ASCONF handling at the receiver.
This patch fixes the problem that the original code cannot delete
the remote address where the corresponding transport is currently
directed, even when the ASCONF is sent from the other address (this
situation happens when the single-homed sender transmits ASCONF
with ADD and DEL.)
Signed-off-by: Michio Honda <micchie@sfc.wide.ad.jp>
Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
---
net/sctp/sm_make_chunk.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c
index 58eb27f..3740603 100644
--- a/net/sctp/sm_make_chunk.c
+++ b/net/sctp/sm_make_chunk.c
@@ -3014,7 +3014,7 @@ static __be16 sctp_process_asconf_param(struct sctp_association *asoc,
* an Error Cause TLV set to the new error code 'Request to
* Delete Source IP Address'
*/
- if (sctp_cmp_addr_exact(sctp_source(asconf), &addr))
+ if (sctp_cmp_addr_exact(&asconf->source, &addr))
return SCTP_ERROR_DEL_SRC_IP;
/* Section 4.2.2
--
1.7.3.2
^ permalink raw reply related
* [PATCH 0/6] sctp: Auto-ASCONF patch series
From: Michio Honda @ 2011-04-27 8:27 UTC (permalink / raw)
To: netdev; +Cc: Honda Michio, YOSHIFUJI Hideaki, Wei Yongjun
>From 9ede9db0ec4b03d3061a5bfed78328cb5528b908 Mon Sep 17 00:00:00 2001
From: Michio Honda <micchie@sfc.wide.ad.jp>
Date: Wed, 27 Apr 2011 17:16:21 +0900
Subject: [PATCH 0/6] sctp: Auto-ASCONF patch series
Series of 6 patches to support auto_asconf and the other related functionalities that auto_asconf relies on.
Michio Honda (5):
sctp: Add ADD/DEL ASCONF handling at the receiver.
sctp: Add Auto-ASCONF support (core).
sctp: Add sysctl support for Auto-ASCONF.
sctp: Add socket option operation for Auto-ASCONF.
sctp: sctp: Add ASCONF operation on the single-homed host
YOSHIFUJI Hideaki (1):
sctp: Allow regular C expression in 4th argument for
SCTP_DEBUG_PRINTK_IPADDR macro.
include/net/sctp/sctp.h | 11 ++-
include/net/sctp/structs.h | 17 +++++
include/net/sctp/user.h | 1 +
net/sctp/associola.c | 6 ++
net/sctp/bind_addr.c | 15 ++++
net/sctp/ipv6.c | 9 +++
net/sctp/outqueue.c | 13 ++++
net/sctp/protocol.c | 151 ++++++++++++++++++++++++++++++++++++++++++-
net/sctp/sm_make_chunk.c | 40 +++++++++++-
net/sctp/socket.c | 156 +++++++++++++++++++++++++++++++++++++++++---
net/sctp/sysctl.c | 7 ++
11 files changed, 411 insertions(+), 15 deletions(-)
--
1.7.3.2
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox