* [PATCH net-next-2.6 0/5 v2] SCTP updates for net-next-2.6
From: Wei Yongjun @ 2011-04-27 7:35 UTC (permalink / raw)
To: David Miller; +Cc: netdev@vger.kernel.org, lksctp
Hi David
Here is a set of SCTP patches for net-next-2.6, the last part
from vlad's lksctp-dev tree, update SCTP IPv6 routing and IPSec
issues. Please apply.
Changelog:
- redo the intermediate builds test and function test.
- remove useless ->dst_saddr member of sctp_pf
- merge some fix for original patch
Vlad Yasevich (4):
sctp: cache the ipv6 source after route lookup
sctp: make sctp over IPv6 work with IPsec
sctp: remove useless arguments from get_saddr() call
sctp: clean up route lookup calls
Weixing Shi (1):
sctp: fix sctp to work with ipv6 source address routing
include/net/sctp/structs.h | 18 ++---
net/sctp/ipv6.c | 183 +++++++++++++++++++++++++------------------
net/sctp/protocol.c | 54 ++++++-------
net/sctp/socket.c | 2 +-
net/sctp/transport.c | 28 ++++---
5 files changed, 153 insertions(+), 132 deletions(-)
^ permalink raw reply
* [PATCH net-next-2.6 1/5 v2] sctp: fix sctp to work with ipv6 source address routing
From: Wei Yongjun @ 2011-04-27 7:36 UTC (permalink / raw)
To: David Miller; +Cc: netdev@vger.kernel.org, lksctp
In-Reply-To: <4DB7C73D.3000406@cn.fujitsu.com>
From: Weixing Shi <Weixing.Shi@windriver.com>
In the below test case, using the source address routing,
sctp can not work.
Node-A
1)ifconfig eth0 inet6 add 2001:1::1/64
2)ip -6 rule add from 2001:1::1 table 100 pref 100
3)ip -6 route add 2001:2::1 dev eth0 table 100
4)sctp_darn -H 2001:1::1 -P 250 -l &
Node-B
1)ifconfig eth0 inet6 add 2001:2::1/64
2)ip -6 rule add from 2001:2::1 table 100 pref 100
3)ip -6 route add 2001:1::1 dev eth0 table 100
4)sctp_darn -H 2001:2::1 -P 250 -h 2001:1::1 -p 250 -s
root cause:
Node-A and Node-B use the source address routing, and
at begining, source address will be NULL,sctp will
search the routing table by the destination address,
because using the source address routing table, and
the result dst_entry will be NULL.
solution:
walk through the bind address list to get the source
address and then lookup the routing table again to get
the correct dst_entry.
Signed-off-by: Weixing Shi <Weixing.Shi@windriver.com>
Signed-off-by: Vlad Yasevich <vladislav.yasevich@hp.com>
Signed-off-by: Wei Yongjun <yjwei@cn.fujitsu.com>
---
net/sctp/ipv6.c | 44 +++++++++++++++++++++++++++++++++++++++++++-
1 files changed, 43 insertions(+), 1 deletions(-)
diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c
index 321f175..3a571d6 100644
--- a/net/sctp/ipv6.c
+++ b/net/sctp/ipv6.c
@@ -80,6 +80,9 @@
#include <asm/uaccess.h>
+static inline int sctp_v6_addr_match_len(union sctp_addr *s1,
+ union sctp_addr *s2);
+
/* Event handler for inet6 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
@@ -244,8 +247,14 @@ static struct dst_entry *sctp_v6_get_dst(struct sctp_association *asoc,
union sctp_addr *daddr,
union sctp_addr *saddr)
{
- struct dst_entry *dst;
+ struct dst_entry *dst = NULL;
struct flowi6 fl6;
+ struct sctp_bind_addr *bp;
+ struct sctp_sockaddr_entry *laddr;
+ union sctp_addr *baddr = NULL;
+ __u8 matchlen = 0;
+ __u8 bmatchlen;
+ sctp_scope_t scope;
memset(&fl6, 0, sizeof(fl6));
ipv6_addr_copy(&fl6.daddr, &daddr->v6.sin6_addr);
@@ -261,6 +270,39 @@ static struct dst_entry *sctp_v6_get_dst(struct sctp_association *asoc,
}
dst = ip6_route_output(&init_net, NULL, &fl6);
+ if (!asoc || saddr)
+ goto out;
+
+ if (dst->error) {
+ dst_release(dst);
+ dst = NULL;
+ bp = &asoc->base.bind_addr;
+ scope = sctp_scope(daddr);
+ /* Walk through the bind address list and try to get a dst that
+ * matches a bind address as the source address.
+ */
+ rcu_read_lock();
+ list_for_each_entry_rcu(laddr, &bp->address_list, list) {
+ if (!laddr->valid)
+ continue;
+ if ((laddr->state == SCTP_ADDR_SRC) &&
+ (laddr->a.sa.sa_family == AF_INET6) &&
+ (scope <= sctp_scope(&laddr->a))) {
+ bmatchlen = sctp_v6_addr_match_len(daddr,
+ &laddr->a);
+ if (!baddr || (matchlen < bmatchlen)) {
+ baddr = &laddr->a;
+ matchlen = bmatchlen;
+ }
+ }
+ }
+ rcu_read_unlock();
+ if (baddr) {
+ ipv6_addr_copy(&fl6.saddr, &baddr->v6.sin6_addr);
+ dst = ip6_route_output(&init_net, NULL, &fl6);
+ }
+ }
+out:
if (!dst->error) {
struct rt6_info *rt;
rt = (struct rt6_info *)dst;
--
1.6.5.2
^ permalink raw reply related
* Re: [Bugme-new] [Bug 33842] New: NULL pointer dereference in ip_fragment
From: Eric Dumazet @ 2011-04-27 7:41 UTC (permalink / raw)
To: Tomas Carnecky; +Cc: Bandan Das, David Miller, netdev, akpm
In-Reply-To: <4DB7C44D.60306@dbservice.com>
Le mercredi 27 avril 2011 à 09:22 +0200, Tomas Carnecky a écrit :
> On 4/26/11 11:24 PM, Eric Dumazet wrote:
> > Le mardi 26 avril 2011 à 17:19 -0400, Bandan Das a écrit :
> >> Yeah, I just rechecked and this is already in Linus' tree. So, Tomas you can
> >> either try pulling in those changes or you can apply this patch and see
> >> if it makes any difference. Thanks!
> > Better pull Linus tree because there is another patch involved.
> >
> > (commits c65353daf137dd41f3ede3baf62d561fca076228
> > ip: ip_options_compile() resilient to NULL skb route
>
> Still getting that error (on rc4-00245-g4175242, which includes that
> commit).
>
Could you send us a complete trace ?
One way to get one is to use netconsole (provided you have another
machine )
grep NETCONSOLE .config
CONFIG_NETCONSOLE=y
Add on your boot command
netconsole=4444@192.168.20.108/eth0,4444@192.168.20.112/00:1e:0b:ec:c3:e4
messages sent to host 192.168.20.112 udp port 4444, mac addr 00:1e:0b:ec:c3:e4, on eth0
then on 192.168.20.112 start a netcat listening on udp port 4444 to get
a copy of messages.
netcat -l -u -p 4444
Complete doc on Documentation/networking/netconsole.txt
Thanks !
^ permalink raw reply
* [PATCH net-next-2.6 2/5 v2] sctp: cache the ipv6 source after route lookup
From: Wei Yongjun @ 2011-04-27 7:51 UTC (permalink / raw)
To: David Miller; +Cc: netdev@vger.kernel.org, lksctp
In-Reply-To: <4DB7C73D.3000406@cn.fujitsu.com>
From: Vlad Yasevich <vladislav.yasevich@hp.com>
The ipv6 routing lookup does give us a source address,
but instead of filling it into the dst, it's stored in
the flowi. We can use that instead of going through the
entire source address selection again.
Also the useless ->dst_saddr member of sctp_pf is removed.
And sctp_v6_dst_saddr() is removed, instead by introduce
sctp_v6_to_addr(), which can be reused to cleanup some dup
code.
Signed-off-by: Vlad Yasevich <vladislav.yasevich@hp.com>
Signed-off-by: Wei Yongjun <yjwei@cn.fujitsu.com>
---
include/net/sctp/structs.h | 14 ++--
net/sctp/ipv6.c | 161 ++++++++++++++++++++------------------------
net/sctp/protocol.c | 47 ++++++-------
net/sctp/socket.c | 2 +-
net/sctp/transport.c | 15 +++--
5 files changed, 112 insertions(+), 127 deletions(-)
diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h
index 5c9bada..1d465d6 100644
--- a/include/net/sctp/structs.h
+++ b/include/net/sctp/structs.h
@@ -566,17 +566,15 @@ struct sctp_af {
int __user *optlen);
struct dst_entry *(*get_dst) (struct sctp_association *asoc,
union sctp_addr *daddr,
- union sctp_addr *saddr);
+ union sctp_addr *saddr,
+ struct flowi *fl,
+ struct sock *sk);
void (*get_saddr) (struct sctp_sock *sk,
- struct sctp_association *asoc,
- struct dst_entry *dst,
+ struct sctp_transport *t,
union sctp_addr *daddr,
- union sctp_addr *saddr);
+ struct flowi *fl);
void (*copy_addrlist) (struct list_head *,
struct net_device *);
- void (*dst_saddr) (union sctp_addr *saddr,
- struct dst_entry *dst,
- __be16 port);
int (*cmp_addr) (const union sctp_addr *addr1,
const union sctp_addr *addr2);
void (*addr_copy) (union sctp_addr *dst,
@@ -1061,7 +1059,7 @@ void sctp_transport_set_owner(struct sctp_transport *,
struct sctp_association *);
void sctp_transport_route(struct sctp_transport *, union sctp_addr *,
struct sctp_sock *);
-void sctp_transport_pmtu(struct sctp_transport *);
+void sctp_transport_pmtu(struct sctp_transport *, struct sock *sk);
void sctp_transport_free(struct sctp_transport *);
void sctp_transport_reset_timers(struct sctp_transport *);
void sctp_transport_hold(struct sctp_transport *);
diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c
index 3a571d6..51c048d 100644
--- a/net/sctp/ipv6.c
+++ b/net/sctp/ipv6.c
@@ -82,6 +82,10 @@
static inline int sctp_v6_addr_match_len(union sctp_addr *s1,
union sctp_addr *s2);
+static void sctp_v6_to_addr(union sctp_addr *addr, struct in6_addr *saddr,
+ __be16 port);
+static int sctp_v6_cmp_addr(const union sctp_addr *addr1,
+ const union sctp_addr *addr2);
/* Event handler for inet6 address addition/deletion events.
* The sctp_local_addr_list needs to be protocted by a spin lock since
@@ -245,73 +249,99 @@ static int sctp_v6_xmit(struct sk_buff *skb, struct sctp_transport *transport)
*/
static struct dst_entry *sctp_v6_get_dst(struct sctp_association *asoc,
union sctp_addr *daddr,
- union sctp_addr *saddr)
+ union sctp_addr *saddr,
+ struct flowi *fl,
+ struct sock *sk)
{
struct dst_entry *dst = NULL;
- struct flowi6 fl6;
+ struct flowi6 *fl6 = &fl->u.ip6;
struct sctp_bind_addr *bp;
struct sctp_sockaddr_entry *laddr;
union sctp_addr *baddr = NULL;
+ union sctp_addr dst_saddr;
__u8 matchlen = 0;
__u8 bmatchlen;
sctp_scope_t scope;
+ int err = 0;
- memset(&fl6, 0, sizeof(fl6));
- ipv6_addr_copy(&fl6.daddr, &daddr->v6.sin6_addr);
+ memset(fl6, 0, sizeof(struct flowi6));
+ ipv6_addr_copy(&fl6->daddr, &daddr->v6.sin6_addr);
if (ipv6_addr_type(&daddr->v6.sin6_addr) & IPV6_ADDR_LINKLOCAL)
- fl6.flowi6_oif = daddr->v6.sin6_scope_id;
+ fl6->flowi6_oif = daddr->v6.sin6_scope_id;
- SCTP_DEBUG_PRINTK("%s: DST=%pI6 ", __func__, &fl6.daddr);
+ SCTP_DEBUG_PRINTK("%s: DST=%pI6 ", __func__, &fl6->daddr);
if (saddr) {
- ipv6_addr_copy(&fl6.saddr, &saddr->v6.sin6_addr);
- SCTP_DEBUG_PRINTK("SRC=%pI6 - ", &fl6.saddr);
+ ipv6_addr_copy(&fl6->saddr, &saddr->v6.sin6_addr);
+ SCTP_DEBUG_PRINTK("SRC=%pI6 - ", &fl6->saddr);
}
- dst = ip6_route_output(&init_net, NULL, &fl6);
+ err = ip6_dst_lookup(sk, &dst, fl6);
if (!asoc || saddr)
goto out;
- if (dst->error) {
- dst_release(dst);
- dst = NULL;
- bp = &asoc->base.bind_addr;
- scope = sctp_scope(daddr);
- /* Walk through the bind address list and try to get a dst that
- * matches a bind address as the source address.
+ bp = &asoc->base.bind_addr;
+ scope = sctp_scope(daddr);
+ /* ip6_dst_lookup has filled in the fl6->saddr for us. Check
+ * to see if we can use it.
+ */
+ if (!err) {
+ /* Walk through the bind address list and look for a bind
+ * address that matches the source address of the returned dst.
*/
+ sctp_v6_to_addr(&dst_saddr, &fl6->saddr, htons(bp->port));
rcu_read_lock();
list_for_each_entry_rcu(laddr, &bp->address_list, list) {
- if (!laddr->valid)
+ if (!laddr->valid || (laddr->state != SCTP_ADDR_SRC))
continue;
- if ((laddr->state == SCTP_ADDR_SRC) &&
- (laddr->a.sa.sa_family == AF_INET6) &&
- (scope <= sctp_scope(&laddr->a))) {
- bmatchlen = sctp_v6_addr_match_len(daddr,
- &laddr->a);
- if (!baddr || (matchlen < bmatchlen)) {
- baddr = &laddr->a;
- matchlen = bmatchlen;
- }
+
+ /* Do not compare against v4 addrs */
+ if ((laddr->a.sa.sa_family == AF_INET6) &&
+ (sctp_v6_cmp_addr(&dst_saddr, &laddr->a))) {
+ rcu_read_unlock();
+ goto out;
}
}
rcu_read_unlock();
- if (baddr) {
- ipv6_addr_copy(&fl6.saddr, &baddr->v6.sin6_addr);
- dst = ip6_route_output(&init_net, NULL, &fl6);
+ /* None of the bound addresses match the source address of the
+ * dst. So release it.
+ */
+ dst_release(dst);
+ dst = NULL;
+ }
+
+ /* Walk through the bind address list and try to get the
+ * best source address for a given destination.
+ */
+ rcu_read_lock();
+ list_for_each_entry_rcu(laddr, &bp->address_list, list) {
+ if (!laddr->valid && laddr->state != SCTP_ADDR_SRC)
+ continue;
+ if ((laddr->a.sa.sa_family == AF_INET6) &&
+ (scope <= sctp_scope(&laddr->a))) {
+ bmatchlen = sctp_v6_addr_match_len(daddr, &laddr->a);
+ if (!baddr || (matchlen < bmatchlen)) {
+ baddr = &laddr->a;
+ matchlen = bmatchlen;
+ }
}
}
+ rcu_read_unlock();
+ if (baddr) {
+ ipv6_addr_copy(&fl6->saddr, &baddr->v6.sin6_addr);
+ err = ip6_dst_lookup(sk, &dst, fl6);
+ }
+
out:
- if (!dst->error) {
+ if (!err) {
struct rt6_info *rt;
rt = (struct rt6_info *)dst;
SCTP_DEBUG_PRINTK("rt6_dst:%pI6 rt6_src:%pI6\n",
- &rt->rt6i_dst.addr, &rt->rt6i_src.addr);
+ &rt->rt6i_dst.addr, &fl6->saddr);
return dst;
}
SCTP_DEBUG_PRINTK("NO ROUTE\n");
- dst_release(dst);
return NULL;
}
@@ -328,64 +358,21 @@ static inline int sctp_v6_addr_match_len(union sctp_addr *s1,
* and asoc's bind address list.
*/
static void sctp_v6_get_saddr(struct sctp_sock *sk,
- struct sctp_association *asoc,
- struct dst_entry *dst,
+ struct sctp_transport *t,
union sctp_addr *daddr,
- union sctp_addr *saddr)
+ struct flowi *fl)
{
- struct sctp_bind_addr *bp;
- struct sctp_sockaddr_entry *laddr;
- sctp_scope_t scope;
- union sctp_addr *baddr = NULL;
- __u8 matchlen = 0;
- __u8 bmatchlen;
+ struct flowi6 *fl6 = &fl->u.ip6;
+ union sctp_addr *saddr = &t->saddr;
SCTP_DEBUG_PRINTK("%s: asoc:%p dst:%p daddr:%pI6 ",
- __func__, asoc, dst, &daddr->v6.sin6_addr);
-
- if (!asoc) {
- ipv6_dev_get_saddr(sock_net(sctp_opt2sk(sk)),
- dst ? ip6_dst_idev(dst)->dev : NULL,
- &daddr->v6.sin6_addr,
- inet6_sk(&sk->inet.sk)->srcprefs,
- &saddr->v6.sin6_addr);
- SCTP_DEBUG_PRINTK("saddr from ipv6_get_saddr: %pI6\n",
- &saddr->v6.sin6_addr);
- return;
- }
-
- scope = sctp_scope(daddr);
+ __func__, t->asoc, t->dst, &daddr->v6.sin6_addr);
- bp = &asoc->base.bind_addr;
-
- /* Go through the bind address list and find the best source address
- * that matches the scope of the destination address.
- */
- rcu_read_lock();
- list_for_each_entry_rcu(laddr, &bp->address_list, list) {
- if (!laddr->valid)
- continue;
- if ((laddr->state == SCTP_ADDR_SRC) &&
- (laddr->a.sa.sa_family == AF_INET6) &&
- (scope <= sctp_scope(&laddr->a))) {
- bmatchlen = sctp_v6_addr_match_len(daddr, &laddr->a);
- if (!baddr || (matchlen < bmatchlen)) {
- baddr = &laddr->a;
- matchlen = bmatchlen;
- }
- }
- }
- if (baddr) {
- memcpy(saddr, baddr, sizeof(union sctp_addr));
- SCTP_DEBUG_PRINTK("saddr: %pI6\n", &saddr->v6.sin6_addr);
- } else {
- pr_err("%s: asoc:%p Could not find a valid source "
- "address for the dest:%pI6\n",
- __func__, asoc, &daddr->v6.sin6_addr);
+ if (t->dst) {
+ saddr->v6.sin6_family = AF_INET6;
+ ipv6_addr_copy(&saddr->v6.sin6_addr, &fl6->saddr);
}
-
- rcu_read_unlock();
}
/* Make a copy of all potential local addresses. */
@@ -507,14 +494,13 @@ static int sctp_v6_to_addr_param(const union sctp_addr *addr,
return length;
}
-/* Initialize a sctp_addr from a dst_entry. */
-static void sctp_v6_dst_saddr(union sctp_addr *addr, struct dst_entry *dst,
+/* Initialize a sctp_addr from struct in6_addr. */
+static void sctp_v6_to_addr(union sctp_addr *addr, struct in6_addr *saddr,
__be16 port)
{
- struct rt6_info *rt = (struct rt6_info *)dst;
addr->sa.sa_family = AF_INET6;
addr->v6.sin6_port = port;
- ipv6_addr_copy(&addr->v6.sin6_addr, &rt->rt6i_src.addr);
+ ipv6_addr_copy(&addr->v6.sin6_addr, saddr);
}
/* Compare addresses exactly.
@@ -1001,7 +987,6 @@ static struct sctp_af sctp_af_inet6 = {
.to_sk_daddr = sctp_v6_to_sk_daddr,
.from_addr_param = sctp_v6_from_addr_param,
.to_addr_param = sctp_v6_to_addr_param,
- .dst_saddr = sctp_v6_dst_saddr,
.cmp_addr = sctp_v6_cmp_addr,
.scope = sctp_v6_scope,
.addr_valid = sctp_v6_addr_valid,
diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c
index d5bf91d..3421645 100644
--- a/net/sctp/protocol.c
+++ b/net/sctp/protocol.c
@@ -465,33 +465,35 @@ static sctp_scope_t sctp_v4_scope(union sctp_addr *addr)
*/
static struct dst_entry *sctp_v4_get_dst(struct sctp_association *asoc,
union sctp_addr *daddr,
- union sctp_addr *saddr)
+ union sctp_addr *saddr,
+ struct flowi *fl,
+ struct sock *sk)
{
struct rtable *rt;
- struct flowi4 fl4;
+ struct flowi4 *fl4 = &fl->u.ip4;
struct sctp_bind_addr *bp;
struct sctp_sockaddr_entry *laddr;
struct dst_entry *dst = NULL;
union sctp_addr dst_saddr;
- memset(&fl4, 0x0, sizeof(struct flowi4));
- fl4.daddr = daddr->v4.sin_addr.s_addr;
- fl4.fl4_dport = daddr->v4.sin_port;
- fl4.flowi4_proto = IPPROTO_SCTP;
+ memset(fl4, 0x0, sizeof(struct flowi4));
+ fl4->daddr = daddr->v4.sin_addr.s_addr;
+ fl4->fl4_dport = daddr->v4.sin_port;
+ fl4->flowi4_proto = IPPROTO_SCTP;
if (asoc) {
- fl4.flowi4_tos = RT_CONN_FLAGS(asoc->base.sk);
- fl4.flowi4_oif = asoc->base.sk->sk_bound_dev_if;
- fl4.fl4_sport = htons(asoc->base.bind_addr.port);
+ fl4->flowi4_tos = RT_CONN_FLAGS(asoc->base.sk);
+ fl4->flowi4_oif = asoc->base.sk->sk_bound_dev_if;
+ fl4->fl4_sport = htons(asoc->base.bind_addr.port);
}
if (saddr) {
- fl4.saddr = saddr->v4.sin_addr.s_addr;
- fl4.fl4_sport = saddr->v4.sin_port;
+ fl4->saddr = saddr->v4.sin_addr.s_addr;
+ fl4->fl4_sport = saddr->v4.sin_port;
}
SCTP_DEBUG_PRINTK("%s: DST:%pI4, SRC:%pI4 - ",
- __func__, &fl4.daddr, &fl4.saddr);
+ __func__, &fl4->daddr, &fl4->saddr);
- rt = ip_route_output_key(&init_net, &fl4);
+ rt = ip_route_output_key(&init_net, fl4);
if (!IS_ERR(rt))
dst = &rt->dst;
@@ -533,9 +535,9 @@ static struct dst_entry *sctp_v4_get_dst(struct sctp_association *asoc,
continue;
if ((laddr->state == SCTP_ADDR_SRC) &&
(AF_INET == laddr->a.sa.sa_family)) {
- fl4.saddr = laddr->a.v4.sin_addr.s_addr;
- fl4.fl4_sport = laddr->a.v4.sin_port;
- rt = ip_route_output_key(&init_net, &fl4);
+ fl4->saddr = laddr->a.v4.sin_addr.s_addr;
+ fl4->fl4_sport = laddr->a.v4.sin_port;
+ rt = ip_route_output_key(&init_net, fl4);
if (!IS_ERR(rt)) {
dst = &rt->dst;
goto out_unlock;
@@ -559,19 +561,15 @@ out:
* to cache it separately and hence this is an empty routine.
*/
static void sctp_v4_get_saddr(struct sctp_sock *sk,
- struct sctp_association *asoc,
- struct dst_entry *dst,
+ struct sctp_transport *t,
union sctp_addr *daddr,
- union sctp_addr *saddr)
+ struct flowi *fl)
{
- struct rtable *rt = (struct rtable *)dst;
-
- if (!asoc)
- return;
+ union sctp_addr *saddr = &t->saddr;
+ struct rtable *rt = (struct rtable *)t->dst;
if (rt) {
saddr->v4.sin_family = AF_INET;
- saddr->v4.sin_port = htons(asoc->base.bind_addr.port);
saddr->v4.sin_addr.s_addr = rt->rt_src;
}
}
@@ -950,7 +948,6 @@ static struct sctp_af sctp_af_inet = {
.to_sk_daddr = sctp_v4_to_sk_daddr,
.from_addr_param = sctp_v4_from_addr_param,
.to_addr_param = sctp_v4_to_addr_param,
- .dst_saddr = sctp_v4_dst_saddr,
.cmp_addr = sctp_v4_cmp_addr,
.addr_valid = sctp_v4_addr_valid,
.inaddr_any = sctp_v4_inaddr_any,
diff --git a/net/sctp/socket.c b/net/sctp/socket.c
index f694ee1..33d9ee6 100644
--- a/net/sctp/socket.c
+++ b/net/sctp/socket.c
@@ -2287,7 +2287,7 @@ static int sctp_apply_peer_addr_params(struct sctp_paddrparams *params,
trans->param_flags =
(trans->param_flags & ~SPP_PMTUD) | pmtud_change;
if (update) {
- sctp_transport_pmtu(trans);
+ sctp_transport_pmtu(trans, sctp_opt2sk(sp));
sctp_assoc_sync_pmtu(asoc);
}
} else if (asoc) {
diff --git a/net/sctp/transport.c b/net/sctp/transport.c
index d3ae493..2544b9b 100644
--- a/net/sctp/transport.c
+++ b/net/sctp/transport.c
@@ -211,11 +211,15 @@ void sctp_transport_set_owner(struct sctp_transport *transport,
}
/* Initialize the pmtu of a transport. */
-void sctp_transport_pmtu(struct sctp_transport *transport)
+void sctp_transport_pmtu(struct sctp_transport *transport, struct sock *sk)
{
struct dst_entry *dst;
+ struct flowi fl;
- dst = transport->af_specific->get_dst(NULL, &transport->ipaddr, NULL);
+ dst = transport->af_specific->get_dst(transport->asoc,
+ &transport->ipaddr,
+ &transport->saddr,
+ &fl, sk);
if (dst) {
transport->pathmtu = dst_mtu(dst);
@@ -272,15 +276,16 @@ void sctp_transport_route(struct sctp_transport *transport,
struct sctp_af *af = transport->af_specific;
union sctp_addr *daddr = &transport->ipaddr;
struct dst_entry *dst;
+ struct flowi fl;
- dst = af->get_dst(asoc, daddr, saddr);
+ dst = af->get_dst(asoc, daddr, saddr, &fl, sctp_opt2sk(opt));
+ transport->dst = dst;
if (saddr)
memcpy(&transport->saddr, saddr, sizeof(union sctp_addr));
else
- af->get_saddr(opt, asoc, dst, daddr, &transport->saddr);
+ af->get_saddr(opt, transport, daddr, &fl);
- transport->dst = dst;
if ((transport->param_flags & SPP_PMTUD_DISABLE) && transport->pathmtu) {
return;
}
--
1.6.5.2
^ permalink raw reply related
* [PATCH net-next-2.6 3/5] sctp: make sctp over IPv6 work with IPsec
From: Wei Yongjun @ 2011-04-27 7:52 UTC (permalink / raw)
To: David Miller; +Cc: netdev@vger.kernel.org, lksctp
In-Reply-To: <4DB7C73D.3000406@cn.fujitsu.com>
From: Vlad Yasevich <vladislav.yasevich@hp.com>
SCTP never called xfrm_output after it's v6 route lookups so
that never really worked with ipsec. Additioanlly, we never
passed port nubmers and protocol in the flowi, so any port
based policies were never applied as well. Now that we can
fixed ipv6 routing lookup code, using ip6_dst_lookup_flow()
and pass port numbers.
Signed-off-by: Vlad Yasevich <vladislav.yasevich@hp.com>
Signed-off-by: Wei Yongjun <yjwei@cn.fujitsu.com>
---
net/sctp/ipv6.c | 16 +++++++++++-----
1 files changed, 11 insertions(+), 5 deletions(-)
diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c
index 51c048d..593c801 100644
--- a/net/sctp/ipv6.c
+++ b/net/sctp/ipv6.c
@@ -262,22 +262,27 @@ static struct dst_entry *sctp_v6_get_dst(struct sctp_association *asoc,
__u8 matchlen = 0;
__u8 bmatchlen;
sctp_scope_t scope;
- int err = 0;
memset(fl6, 0, sizeof(struct flowi6));
ipv6_addr_copy(&fl6->daddr, &daddr->v6.sin6_addr);
+ fl6->fl6_dport = daddr->v6.sin6_port;
+ fl6->flowi6_proto = IPPROTO_SCTP;
if (ipv6_addr_type(&daddr->v6.sin6_addr) & IPV6_ADDR_LINKLOCAL)
fl6->flowi6_oif = daddr->v6.sin6_scope_id;
SCTP_DEBUG_PRINTK("%s: DST=%pI6 ", __func__, &fl6->daddr);
+ if (asoc)
+ fl6->fl6_sport = htons(asoc->base.bind_addr.port);
+
if (saddr) {
ipv6_addr_copy(&fl6->saddr, &saddr->v6.sin6_addr);
+ fl6->fl6_sport = saddr->v6.sin6_port;
SCTP_DEBUG_PRINTK("SRC=%pI6 - ", &fl6->saddr);
}
- err = ip6_dst_lookup(sk, &dst, fl6);
+ dst = ip6_dst_lookup_flow(sk, fl6, NULL, false);
if (!asoc || saddr)
goto out;
@@ -286,7 +291,7 @@ static struct dst_entry *sctp_v6_get_dst(struct sctp_association *asoc,
/* ip6_dst_lookup has filled in the fl6->saddr for us. Check
* to see if we can use it.
*/
- if (!err) {
+ if (!IS_ERR(dst)) {
/* Walk through the bind address list and look for a bind
* address that matches the source address of the returned dst.
*/
@@ -330,11 +335,12 @@ static struct dst_entry *sctp_v6_get_dst(struct sctp_association *asoc,
rcu_read_unlock();
if (baddr) {
ipv6_addr_copy(&fl6->saddr, &baddr->v6.sin6_addr);
- err = ip6_dst_lookup(sk, &dst, fl6);
+ fl6->fl6_sport = baddr->v6.sin6_port;
+ dst = ip6_dst_lookup_flow(sk, fl6, NULL, false);
}
out:
- if (!err) {
+ if (!IS_ERR(dst)) {
struct rt6_info *rt;
rt = (struct rt6_info *)dst;
SCTP_DEBUG_PRINTK("rt6_dst:%pI6 rt6_src:%pI6\n",
--
1.6.5.2
^ permalink raw reply related
* Re: [Bugme-new] [Bug 33842] New: NULL pointer dereference in ip_fragment
From: Tomas Carnecky @ 2011-04-27 7:22 UTC (permalink / raw)
To: Eric Dumazet; +Cc: Bandan Das, David Miller, netdev, akpm
In-Reply-To: <1303853047.2699.15.camel@edumazet-laptop>
On 4/26/11 11:24 PM, Eric Dumazet wrote:
> Le mardi 26 avril 2011 à 17:19 -0400, Bandan Das a écrit :
>> Yeah, I just rechecked and this is already in Linus' tree. So, Tomas you can
>> either try pulling in those changes or you can apply this patch and see
>> if it makes any difference. Thanks!
> Better pull Linus tree because there is another patch involved.
>
> (commits c65353daf137dd41f3ede3baf62d561fca076228
> ip: ip_options_compile() resilient to NULL skb route
Still getting that error (on rc4-00245-g4175242, which includes that
commit).
^ permalink raw reply
* [PATCH net-next-2.6 4/5 v2] sctp: remove useless arguments from get_saddr() call
From: Wei Yongjun @ 2011-04-27 7:53 UTC (permalink / raw)
To: David Miller; +Cc: netdev@vger.kernel.org, lksctp
In-Reply-To: <4DB7C73D.3000406@cn.fujitsu.com>
From: Vlad Yasevich <vladislav.yasevich@hp.com>
There is no point in passing a destination address to
a get_saddr() call.
Signed-off-by: Vlad Yasevich <vladislav.yasevich@hp.com>
Signed-off-by: Wei Yongjun <yjwei@cn.fujitsu.com>
---
include/net/sctp/structs.h | 1 -
net/sctp/ipv6.c | 5 +----
net/sctp/protocol.c | 1 -
net/sctp/transport.c | 2 +-
4 files changed, 2 insertions(+), 7 deletions(-)
diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h
index 1d465d6..bb2f43b 100644
--- a/include/net/sctp/structs.h
+++ b/include/net/sctp/structs.h
@@ -571,7 +571,6 @@ struct sctp_af {
struct sock *sk);
void (*get_saddr) (struct sctp_sock *sk,
struct sctp_transport *t,
- union sctp_addr *daddr,
struct flowi *fl);
void (*copy_addrlist) (struct list_head *,
struct net_device *);
diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c
index 593c801..a1913a4 100644
--- a/net/sctp/ipv6.c
+++ b/net/sctp/ipv6.c
@@ -365,15 +365,12 @@ static inline int sctp_v6_addr_match_len(union sctp_addr *s1,
*/
static void sctp_v6_get_saddr(struct sctp_sock *sk,
struct sctp_transport *t,
- union sctp_addr *daddr,
struct flowi *fl)
{
struct flowi6 *fl6 = &fl->u.ip6;
union sctp_addr *saddr = &t->saddr;
- SCTP_DEBUG_PRINTK("%s: asoc:%p dst:%p daddr:%pI6 ",
- __func__, t->asoc, t->dst, &daddr->v6.sin6_addr);
-
+ SCTP_DEBUG_PRINTK("%s: asoc:%p dst:%p\n", __func__, t->asoc, t->dst);
if (t->dst) {
saddr->v6.sin6_family = AF_INET6;
diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c
index 3421645..68b4c43 100644
--- a/net/sctp/protocol.c
+++ b/net/sctp/protocol.c
@@ -562,7 +562,6 @@ out:
*/
static void sctp_v4_get_saddr(struct sctp_sock *sk,
struct sctp_transport *t,
- union sctp_addr *daddr,
struct flowi *fl)
{
union sctp_addr *saddr = &t->saddr;
diff --git a/net/sctp/transport.c b/net/sctp/transport.c
index 2544b9b..1fbb920 100644
--- a/net/sctp/transport.c
+++ b/net/sctp/transport.c
@@ -284,7 +284,7 @@ void sctp_transport_route(struct sctp_transport *transport,
if (saddr)
memcpy(&transport->saddr, saddr, sizeof(union sctp_addr));
else
- af->get_saddr(opt, transport, daddr, &fl);
+ af->get_saddr(opt, transport, &fl);
if ((transport->param_flags & SPP_PMTUD_DISABLE) && transport->pathmtu) {
return;
--
1.6.5.2
^ permalink raw reply related
* [PATCH net-next-2.6 5/5 v2] sctp: clean up route lookup calls
From: Wei Yongjun @ 2011-04-27 7:54 UTC (permalink / raw)
To: David Miller; +Cc: netdev@vger.kernel.org, lksctp
In-Reply-To: <4DB7C73D.3000406@cn.fujitsu.com>
From: Vlad Yasevich <vladislav.yasevich@hp.com>
Change the call to take the transport parameter and set the
cached 'dst' appropriately inside the get_dst() function calls.
This will allow us in the future to clean up source address
storage as well.
Signed-off-by: Vlad Yasevich <vladislav.yasevich@hp.com>
Signed-off-by: Wei Yongjun <yjwei@cn.fujitsu.com>
---
include/net/sctp/structs.h | 3 +--
net/sctp/ipv6.c | 17 ++++++++---------
net/sctp/protocol.c | 12 +++++-------
net/sctp/transport.c | 23 ++++++++++-------------
4 files changed, 24 insertions(+), 31 deletions(-)
diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h
index bb2f43b..ff3e8cc 100644
--- a/include/net/sctp/structs.h
+++ b/include/net/sctp/structs.h
@@ -564,8 +564,7 @@ struct sctp_af {
int optname,
char __user *optval,
int __user *optlen);
- struct dst_entry *(*get_dst) (struct sctp_association *asoc,
- union sctp_addr *daddr,
+ void (*get_dst) (struct sctp_transport *t,
union sctp_addr *saddr,
struct flowi *fl,
struct sock *sk);
diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c
index a1913a4..500875f 100644
--- a/net/sctp/ipv6.c
+++ b/net/sctp/ipv6.c
@@ -247,17 +247,16 @@ static int sctp_v6_xmit(struct sk_buff *skb, struct sctp_transport *transport)
/* Returns the dst cache entry for the given source and destination ip
* addresses.
*/
-static struct dst_entry *sctp_v6_get_dst(struct sctp_association *asoc,
- union sctp_addr *daddr,
- union sctp_addr *saddr,
- struct flowi *fl,
- struct sock *sk)
+static void sctp_v6_get_dst(struct sctp_transport *t, union sctp_addr *saddr,
+ struct flowi *fl, struct sock *sk)
{
+ struct sctp_association *asoc = t->asoc;
struct dst_entry *dst = NULL;
struct flowi6 *fl6 = &fl->u.ip6;
struct sctp_bind_addr *bp;
struct sctp_sockaddr_entry *laddr;
union sctp_addr *baddr = NULL;
+ union sctp_addr *daddr = &t->ipaddr;
union sctp_addr dst_saddr;
__u8 matchlen = 0;
__u8 bmatchlen;
@@ -270,7 +269,6 @@ static struct dst_entry *sctp_v6_get_dst(struct sctp_association *asoc,
if (ipv6_addr_type(&daddr->v6.sin6_addr) & IPV6_ADDR_LINKLOCAL)
fl6->flowi6_oif = daddr->v6.sin6_scope_id;
-
SCTP_DEBUG_PRINTK("%s: DST=%pI6 ", __func__, &fl6->daddr);
if (asoc)
@@ -343,12 +341,13 @@ out:
if (!IS_ERR(dst)) {
struct rt6_info *rt;
rt = (struct rt6_info *)dst;
+ t->dst = dst;
SCTP_DEBUG_PRINTK("rt6_dst:%pI6 rt6_src:%pI6\n",
&rt->rt6i_dst.addr, &fl6->saddr);
- return dst;
+ } else {
+ t->dst = NULL;
+ SCTP_DEBUG_PRINTK("NO ROUTE\n");
}
- SCTP_DEBUG_PRINTK("NO ROUTE\n");
- return NULL;
}
/* Returns the number of consecutive initial bits that match in the 2 ipv6
diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c
index 68b4c43..9d3f159 100644
--- a/net/sctp/protocol.c
+++ b/net/sctp/protocol.c
@@ -463,17 +463,16 @@ static sctp_scope_t sctp_v4_scope(union sctp_addr *addr)
* addresses. If an association is passed, trys to get a dst entry with a
* source address that matches an address in the bind address list.
*/
-static struct dst_entry *sctp_v4_get_dst(struct sctp_association *asoc,
- union sctp_addr *daddr,
- union sctp_addr *saddr,
- struct flowi *fl,
- struct sock *sk)
+static void sctp_v4_get_dst(struct sctp_transport *t, union sctp_addr *saddr,
+ struct flowi *fl, struct sock *sk)
{
+ struct sctp_association *asoc = t->asoc;
struct rtable *rt;
struct flowi4 *fl4 = &fl->u.ip4;
struct sctp_bind_addr *bp;
struct sctp_sockaddr_entry *laddr;
struct dst_entry *dst = NULL;
+ union sctp_addr *daddr = &t->ipaddr;
union sctp_addr dst_saddr;
memset(fl4, 0x0, sizeof(struct flowi4));
@@ -548,13 +547,12 @@ static struct dst_entry *sctp_v4_get_dst(struct sctp_association *asoc,
out_unlock:
rcu_read_unlock();
out:
+ t->dst = dst;
if (dst)
SCTP_DEBUG_PRINTK("rt_dst:%pI4, rt_src:%pI4\n",
&rt->rt_dst, &rt->rt_src);
else
SCTP_DEBUG_PRINTK("NO ROUTE\n");
-
- return dst;
}
/* For v4, the source address is cached in the route entry(dst). So no need
diff --git a/net/sctp/transport.c b/net/sctp/transport.c
index 1fbb920..d8595dd 100644
--- a/net/sctp/transport.c
+++ b/net/sctp/transport.c
@@ -213,17 +213,17 @@ void sctp_transport_set_owner(struct sctp_transport *transport,
/* Initialize the pmtu of a transport. */
void sctp_transport_pmtu(struct sctp_transport *transport, struct sock *sk)
{
- struct dst_entry *dst;
struct flowi fl;
- dst = transport->af_specific->get_dst(transport->asoc,
- &transport->ipaddr,
- &transport->saddr,
+ /* If we don't have a fresh route, look one up */
+ if (!transport->dst || transport->dst->obsolete > 1) {
+ dst_release(transport->dst);
+ transport->af_specific->get_dst(transport, &transport->saddr,
&fl, sk);
+ }
- if (dst) {
- transport->pathmtu = dst_mtu(dst);
- dst_release(dst);
+ if (transport->dst) {
+ transport->pathmtu = dst_mtu(transport->dst);
} else
transport->pathmtu = SCTP_DEFAULT_MAXSEGMENT;
}
@@ -274,12 +274,9 @@ void sctp_transport_route(struct sctp_transport *transport,
{
struct sctp_association *asoc = transport->asoc;
struct sctp_af *af = transport->af_specific;
- union sctp_addr *daddr = &transport->ipaddr;
- struct dst_entry *dst;
struct flowi fl;
- dst = af->get_dst(asoc, daddr, saddr, &fl, sctp_opt2sk(opt));
- transport->dst = dst;
+ af->get_dst(transport, saddr, &fl, sctp_opt2sk(opt));
if (saddr)
memcpy(&transport->saddr, saddr, sizeof(union sctp_addr));
@@ -289,8 +286,8 @@ void sctp_transport_route(struct sctp_transport *transport,
if ((transport->param_flags & SPP_PMTUD_DISABLE) && transport->pathmtu) {
return;
}
- if (dst) {
- transport->pathmtu = dst_mtu(dst);
+ if (transport->dst) {
+ transport->pathmtu = dst_mtu(transport->dst);
/* Initialize sk->sk_rcv_saddr, if the transport is the
* association's active path for getsockname().
--
1.6.5.2
^ permalink raw reply related
* Re: [PATCH] Applying inappropriate ioctl operation on socket should return ENOTTY
From: Lifeng Sun @ 2011-04-27 8:22 UTC (permalink / raw)
To: linux-kernel; +Cc: netdev
In-Reply-To: <1303887457.2699.60.camel@edumazet-laptop>
On 08:57 Wed 04/27/11 Apr, Eric Dumazet wrote:
> You quote manpage for a library call, not a system call.
okay, let me quote the one for ioctl system call:
man 2 ioctl
int ioctl(int d, int request, ...);
ERRORS
EBADF d is not a valid descriptor.
EFAULT argp references an inaccessible memory area.
EINVAL Request or argp is not valid.
ENOTTY d is not associated with a character special device.
ENOTTY The specified request does not apply to the kind of object
that the descriptor d references.
we see ENOTTY and EFAULT refine EVINAL and it should return ENOTTY or
EFAULT whenever possible rather than EINVAL, otherwise we could always
return EBADF or EINVAL.
Regarding to isatty, well, it's only a library call, isn't it? :-) If
you insist on the significance of the manpage of isatty, there are
also a lot of ioctl operations return ENOTTY, if not less than those
return EINVAL, for inappropriated command and eventually violate the
ERRORS section of the manpage. Certainly we could complain to c
library maintainers.
> If you feel your glibc doesnt implement well this, please complain to
> glibc maintainer.
--
^ permalink raw reply
* [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
* [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 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 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 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 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 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
* 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
* 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
* 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
* [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
* [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 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 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 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 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
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