162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * File: af_phonet.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Phonet protocols family 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Copyright (C) 2008 Nokia Corporation. 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Authors: Sakari Ailus <sakari.ailus@nokia.com> 1062306a36Sopenharmony_ci * Rémi Denis-Courmont 1162306a36Sopenharmony_ci */ 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <linux/kernel.h> 1462306a36Sopenharmony_ci#include <linux/module.h> 1562306a36Sopenharmony_ci#include <linux/slab.h> 1662306a36Sopenharmony_ci#include <asm/unaligned.h> 1762306a36Sopenharmony_ci#include <net/sock.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include <linux/if_phonet.h> 2062306a36Sopenharmony_ci#include <linux/phonet.h> 2162306a36Sopenharmony_ci#include <net/phonet/phonet.h> 2262306a36Sopenharmony_ci#include <net/phonet/pn_dev.h> 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci/* Transport protocol registration */ 2562306a36Sopenharmony_cistatic const struct phonet_protocol *proto_tab[PHONET_NPROTO] __read_mostly; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistatic const struct phonet_protocol *phonet_proto_get(unsigned int protocol) 2862306a36Sopenharmony_ci{ 2962306a36Sopenharmony_ci const struct phonet_protocol *pp; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci if (protocol >= PHONET_NPROTO) 3262306a36Sopenharmony_ci return NULL; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci rcu_read_lock(); 3562306a36Sopenharmony_ci pp = rcu_dereference(proto_tab[protocol]); 3662306a36Sopenharmony_ci if (pp && !try_module_get(pp->prot->owner)) 3762306a36Sopenharmony_ci pp = NULL; 3862306a36Sopenharmony_ci rcu_read_unlock(); 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci return pp; 4162306a36Sopenharmony_ci} 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistatic inline void phonet_proto_put(const struct phonet_protocol *pp) 4462306a36Sopenharmony_ci{ 4562306a36Sopenharmony_ci module_put(pp->prot->owner); 4662306a36Sopenharmony_ci} 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci/* protocol family functions */ 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistatic int pn_socket_create(struct net *net, struct socket *sock, int protocol, 5162306a36Sopenharmony_ci int kern) 5262306a36Sopenharmony_ci{ 5362306a36Sopenharmony_ci struct sock *sk; 5462306a36Sopenharmony_ci struct pn_sock *pn; 5562306a36Sopenharmony_ci const struct phonet_protocol *pnp; 5662306a36Sopenharmony_ci int err; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 5962306a36Sopenharmony_ci return -EPERM; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci if (protocol == 0) { 6262306a36Sopenharmony_ci /* Default protocol selection */ 6362306a36Sopenharmony_ci switch (sock->type) { 6462306a36Sopenharmony_ci case SOCK_DGRAM: 6562306a36Sopenharmony_ci protocol = PN_PROTO_PHONET; 6662306a36Sopenharmony_ci break; 6762306a36Sopenharmony_ci case SOCK_SEQPACKET: 6862306a36Sopenharmony_ci protocol = PN_PROTO_PIPE; 6962306a36Sopenharmony_ci break; 7062306a36Sopenharmony_ci default: 7162306a36Sopenharmony_ci return -EPROTONOSUPPORT; 7262306a36Sopenharmony_ci } 7362306a36Sopenharmony_ci } 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci pnp = phonet_proto_get(protocol); 7662306a36Sopenharmony_ci if (pnp == NULL && 7762306a36Sopenharmony_ci request_module("net-pf-%d-proto-%d", PF_PHONET, protocol) == 0) 7862306a36Sopenharmony_ci pnp = phonet_proto_get(protocol); 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci if (pnp == NULL) 8162306a36Sopenharmony_ci return -EPROTONOSUPPORT; 8262306a36Sopenharmony_ci if (sock->type != pnp->sock_type) { 8362306a36Sopenharmony_ci err = -EPROTONOSUPPORT; 8462306a36Sopenharmony_ci goto out; 8562306a36Sopenharmony_ci } 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci sk = sk_alloc(net, PF_PHONET, GFP_KERNEL, pnp->prot, kern); 8862306a36Sopenharmony_ci if (sk == NULL) { 8962306a36Sopenharmony_ci err = -ENOMEM; 9062306a36Sopenharmony_ci goto out; 9162306a36Sopenharmony_ci } 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci sock_init_data(sock, sk); 9462306a36Sopenharmony_ci sock->state = SS_UNCONNECTED; 9562306a36Sopenharmony_ci sock->ops = pnp->ops; 9662306a36Sopenharmony_ci sk->sk_backlog_rcv = sk->sk_prot->backlog_rcv; 9762306a36Sopenharmony_ci sk->sk_protocol = protocol; 9862306a36Sopenharmony_ci pn = pn_sk(sk); 9962306a36Sopenharmony_ci pn->sobject = 0; 10062306a36Sopenharmony_ci pn->dobject = 0; 10162306a36Sopenharmony_ci pn->resource = 0; 10262306a36Sopenharmony_ci sk->sk_prot->init(sk); 10362306a36Sopenharmony_ci err = 0; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ciout: 10662306a36Sopenharmony_ci phonet_proto_put(pnp); 10762306a36Sopenharmony_ci return err; 10862306a36Sopenharmony_ci} 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_cistatic const struct net_proto_family phonet_proto_family = { 11162306a36Sopenharmony_ci .family = PF_PHONET, 11262306a36Sopenharmony_ci .create = pn_socket_create, 11362306a36Sopenharmony_ci .owner = THIS_MODULE, 11462306a36Sopenharmony_ci}; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci/* Phonet device header operations */ 11762306a36Sopenharmony_cistatic int pn_header_create(struct sk_buff *skb, struct net_device *dev, 11862306a36Sopenharmony_ci unsigned short type, const void *daddr, 11962306a36Sopenharmony_ci const void *saddr, unsigned int len) 12062306a36Sopenharmony_ci{ 12162306a36Sopenharmony_ci u8 *media = skb_push(skb, 1); 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci if (type != ETH_P_PHONET) 12462306a36Sopenharmony_ci return -1; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci if (!saddr) 12762306a36Sopenharmony_ci saddr = dev->dev_addr; 12862306a36Sopenharmony_ci *media = *(const u8 *)saddr; 12962306a36Sopenharmony_ci return 1; 13062306a36Sopenharmony_ci} 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_cistatic int pn_header_parse(const struct sk_buff *skb, unsigned char *haddr) 13362306a36Sopenharmony_ci{ 13462306a36Sopenharmony_ci const u8 *media = skb_mac_header(skb); 13562306a36Sopenharmony_ci *haddr = *media; 13662306a36Sopenharmony_ci return 1; 13762306a36Sopenharmony_ci} 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ciconst struct header_ops phonet_header_ops = { 14062306a36Sopenharmony_ci .create = pn_header_create, 14162306a36Sopenharmony_ci .parse = pn_header_parse, 14262306a36Sopenharmony_ci}; 14362306a36Sopenharmony_ciEXPORT_SYMBOL(phonet_header_ops); 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci/* 14662306a36Sopenharmony_ci * Prepends an ISI header and sends a datagram. 14762306a36Sopenharmony_ci */ 14862306a36Sopenharmony_cistatic int pn_send(struct sk_buff *skb, struct net_device *dev, 14962306a36Sopenharmony_ci u16 dst, u16 src, u8 res) 15062306a36Sopenharmony_ci{ 15162306a36Sopenharmony_ci struct phonethdr *ph; 15262306a36Sopenharmony_ci int err; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci if (skb->len + 2 > 0xffff /* Phonet length field limit */ || 15562306a36Sopenharmony_ci skb->len + sizeof(struct phonethdr) > dev->mtu) { 15662306a36Sopenharmony_ci err = -EMSGSIZE; 15762306a36Sopenharmony_ci goto drop; 15862306a36Sopenharmony_ci } 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci /* Broadcast sending is not implemented */ 16162306a36Sopenharmony_ci if (pn_addr(dst) == PNADDR_BROADCAST) { 16262306a36Sopenharmony_ci err = -EOPNOTSUPP; 16362306a36Sopenharmony_ci goto drop; 16462306a36Sopenharmony_ci } 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci skb_reset_transport_header(skb); 16762306a36Sopenharmony_ci WARN_ON(skb_headroom(skb) & 1); /* HW assumes word alignment */ 16862306a36Sopenharmony_ci skb_push(skb, sizeof(struct phonethdr)); 16962306a36Sopenharmony_ci skb_reset_network_header(skb); 17062306a36Sopenharmony_ci ph = pn_hdr(skb); 17162306a36Sopenharmony_ci ph->pn_rdev = pn_dev(dst); 17262306a36Sopenharmony_ci ph->pn_sdev = pn_dev(src); 17362306a36Sopenharmony_ci ph->pn_res = res; 17462306a36Sopenharmony_ci ph->pn_length = __cpu_to_be16(skb->len + 2 - sizeof(*ph)); 17562306a36Sopenharmony_ci ph->pn_robj = pn_obj(dst); 17662306a36Sopenharmony_ci ph->pn_sobj = pn_obj(src); 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci skb->protocol = htons(ETH_P_PHONET); 17962306a36Sopenharmony_ci skb->priority = 0; 18062306a36Sopenharmony_ci skb->dev = dev; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci if (skb->pkt_type == PACKET_LOOPBACK) { 18362306a36Sopenharmony_ci skb_reset_mac_header(skb); 18462306a36Sopenharmony_ci skb_orphan(skb); 18562306a36Sopenharmony_ci err = netif_rx(skb) ? -ENOBUFS : 0; 18662306a36Sopenharmony_ci } else { 18762306a36Sopenharmony_ci err = dev_hard_header(skb, dev, ntohs(skb->protocol), 18862306a36Sopenharmony_ci NULL, NULL, skb->len); 18962306a36Sopenharmony_ci if (err < 0) { 19062306a36Sopenharmony_ci err = -EHOSTUNREACH; 19162306a36Sopenharmony_ci goto drop; 19262306a36Sopenharmony_ci } 19362306a36Sopenharmony_ci err = dev_queue_xmit(skb); 19462306a36Sopenharmony_ci if (unlikely(err > 0)) 19562306a36Sopenharmony_ci err = net_xmit_errno(err); 19662306a36Sopenharmony_ci } 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci return err; 19962306a36Sopenharmony_cidrop: 20062306a36Sopenharmony_ci kfree_skb(skb); 20162306a36Sopenharmony_ci return err; 20262306a36Sopenharmony_ci} 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_cistatic int pn_raw_send(const void *data, int len, struct net_device *dev, 20562306a36Sopenharmony_ci u16 dst, u16 src, u8 res) 20662306a36Sopenharmony_ci{ 20762306a36Sopenharmony_ci struct sk_buff *skb = alloc_skb(MAX_PHONET_HEADER + len, GFP_ATOMIC); 20862306a36Sopenharmony_ci if (skb == NULL) 20962306a36Sopenharmony_ci return -ENOMEM; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci if (phonet_address_lookup(dev_net(dev), pn_addr(dst)) == 0) 21262306a36Sopenharmony_ci skb->pkt_type = PACKET_LOOPBACK; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci skb_reserve(skb, MAX_PHONET_HEADER); 21562306a36Sopenharmony_ci __skb_put(skb, len); 21662306a36Sopenharmony_ci skb_copy_to_linear_data(skb, data, len); 21762306a36Sopenharmony_ci return pn_send(skb, dev, dst, src, res); 21862306a36Sopenharmony_ci} 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci/* 22162306a36Sopenharmony_ci * Create a Phonet header for the skb and send it out. Returns 22262306a36Sopenharmony_ci * non-zero error code if failed. The skb is freed then. 22362306a36Sopenharmony_ci */ 22462306a36Sopenharmony_ciint pn_skb_send(struct sock *sk, struct sk_buff *skb, 22562306a36Sopenharmony_ci const struct sockaddr_pn *target) 22662306a36Sopenharmony_ci{ 22762306a36Sopenharmony_ci struct net *net = sock_net(sk); 22862306a36Sopenharmony_ci struct net_device *dev; 22962306a36Sopenharmony_ci struct pn_sock *pn = pn_sk(sk); 23062306a36Sopenharmony_ci int err; 23162306a36Sopenharmony_ci u16 src, dst; 23262306a36Sopenharmony_ci u8 daddr, saddr, res; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci src = pn->sobject; 23562306a36Sopenharmony_ci if (target != NULL) { 23662306a36Sopenharmony_ci dst = pn_sockaddr_get_object(target); 23762306a36Sopenharmony_ci res = pn_sockaddr_get_resource(target); 23862306a36Sopenharmony_ci } else { 23962306a36Sopenharmony_ci dst = pn->dobject; 24062306a36Sopenharmony_ci res = pn->resource; 24162306a36Sopenharmony_ci } 24262306a36Sopenharmony_ci daddr = pn_addr(dst); 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci err = -EHOSTUNREACH; 24562306a36Sopenharmony_ci if (sk->sk_bound_dev_if) 24662306a36Sopenharmony_ci dev = dev_get_by_index(net, sk->sk_bound_dev_if); 24762306a36Sopenharmony_ci else if (phonet_address_lookup(net, daddr) == 0) { 24862306a36Sopenharmony_ci dev = phonet_device_get(net); 24962306a36Sopenharmony_ci skb->pkt_type = PACKET_LOOPBACK; 25062306a36Sopenharmony_ci } else if (dst == 0) { 25162306a36Sopenharmony_ci /* Resource routing (small race until phonet_rcv()) */ 25262306a36Sopenharmony_ci struct sock *sk = pn_find_sock_by_res(net, res); 25362306a36Sopenharmony_ci if (sk) { 25462306a36Sopenharmony_ci sock_put(sk); 25562306a36Sopenharmony_ci dev = phonet_device_get(net); 25662306a36Sopenharmony_ci skb->pkt_type = PACKET_LOOPBACK; 25762306a36Sopenharmony_ci } else 25862306a36Sopenharmony_ci dev = phonet_route_output(net, daddr); 25962306a36Sopenharmony_ci } else 26062306a36Sopenharmony_ci dev = phonet_route_output(net, daddr); 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci if (!dev || !(dev->flags & IFF_UP)) 26362306a36Sopenharmony_ci goto drop; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci saddr = phonet_address_get(dev, daddr); 26662306a36Sopenharmony_ci if (saddr == PN_NO_ADDR) 26762306a36Sopenharmony_ci goto drop; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci if (!pn_addr(src)) 27062306a36Sopenharmony_ci src = pn_object(saddr, pn_obj(src)); 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci err = pn_send(skb, dev, dst, src, res); 27362306a36Sopenharmony_ci dev_put(dev); 27462306a36Sopenharmony_ci return err; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_cidrop: 27762306a36Sopenharmony_ci kfree_skb(skb); 27862306a36Sopenharmony_ci dev_put(dev); 27962306a36Sopenharmony_ci return err; 28062306a36Sopenharmony_ci} 28162306a36Sopenharmony_ciEXPORT_SYMBOL(pn_skb_send); 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci/* Do not send an error message in response to an error message */ 28462306a36Sopenharmony_cistatic inline int can_respond(struct sk_buff *skb) 28562306a36Sopenharmony_ci{ 28662306a36Sopenharmony_ci const struct phonethdr *ph; 28762306a36Sopenharmony_ci const struct phonetmsg *pm; 28862306a36Sopenharmony_ci u8 submsg_id; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci if (!pskb_may_pull(skb, 3)) 29162306a36Sopenharmony_ci return 0; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci ph = pn_hdr(skb); 29462306a36Sopenharmony_ci if (ph->pn_res == PN_PREFIX && !pskb_may_pull(skb, 5)) 29562306a36Sopenharmony_ci return 0; 29662306a36Sopenharmony_ci if (ph->pn_res == PN_COMMGR) /* indications */ 29762306a36Sopenharmony_ci return 0; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci ph = pn_hdr(skb); /* re-acquires the pointer */ 30062306a36Sopenharmony_ci pm = pn_msg(skb); 30162306a36Sopenharmony_ci if (pm->pn_msg_id != PN_COMMON_MESSAGE) 30262306a36Sopenharmony_ci return 1; 30362306a36Sopenharmony_ci submsg_id = (ph->pn_res == PN_PREFIX) 30462306a36Sopenharmony_ci ? pm->pn_e_submsg_id : pm->pn_submsg_id; 30562306a36Sopenharmony_ci if (submsg_id != PN_COMM_ISA_ENTITY_NOT_REACHABLE_RESP && 30662306a36Sopenharmony_ci pm->pn_e_submsg_id != PN_COMM_SERVICE_NOT_IDENTIFIED_RESP) 30762306a36Sopenharmony_ci return 1; 30862306a36Sopenharmony_ci return 0; 30962306a36Sopenharmony_ci} 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_cistatic int send_obj_unreachable(struct sk_buff *rskb) 31262306a36Sopenharmony_ci{ 31362306a36Sopenharmony_ci const struct phonethdr *oph = pn_hdr(rskb); 31462306a36Sopenharmony_ci const struct phonetmsg *opm = pn_msg(rskb); 31562306a36Sopenharmony_ci struct phonetmsg resp; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci memset(&resp, 0, sizeof(resp)); 31862306a36Sopenharmony_ci resp.pn_trans_id = opm->pn_trans_id; 31962306a36Sopenharmony_ci resp.pn_msg_id = PN_COMMON_MESSAGE; 32062306a36Sopenharmony_ci if (oph->pn_res == PN_PREFIX) { 32162306a36Sopenharmony_ci resp.pn_e_res_id = opm->pn_e_res_id; 32262306a36Sopenharmony_ci resp.pn_e_submsg_id = PN_COMM_ISA_ENTITY_NOT_REACHABLE_RESP; 32362306a36Sopenharmony_ci resp.pn_e_orig_msg_id = opm->pn_msg_id; 32462306a36Sopenharmony_ci resp.pn_e_status = 0; 32562306a36Sopenharmony_ci } else { 32662306a36Sopenharmony_ci resp.pn_submsg_id = PN_COMM_ISA_ENTITY_NOT_REACHABLE_RESP; 32762306a36Sopenharmony_ci resp.pn_orig_msg_id = opm->pn_msg_id; 32862306a36Sopenharmony_ci resp.pn_status = 0; 32962306a36Sopenharmony_ci } 33062306a36Sopenharmony_ci return pn_raw_send(&resp, sizeof(resp), rskb->dev, 33162306a36Sopenharmony_ci pn_object(oph->pn_sdev, oph->pn_sobj), 33262306a36Sopenharmony_ci pn_object(oph->pn_rdev, oph->pn_robj), 33362306a36Sopenharmony_ci oph->pn_res); 33462306a36Sopenharmony_ci} 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_cistatic int send_reset_indications(struct sk_buff *rskb) 33762306a36Sopenharmony_ci{ 33862306a36Sopenharmony_ci struct phonethdr *oph = pn_hdr(rskb); 33962306a36Sopenharmony_ci static const u8 data[4] = { 34062306a36Sopenharmony_ci 0x00 /* trans ID */, 0x10 /* subscribe msg */, 34162306a36Sopenharmony_ci 0x00 /* subscription count */, 0x00 /* dummy */ 34262306a36Sopenharmony_ci }; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci return pn_raw_send(data, sizeof(data), rskb->dev, 34562306a36Sopenharmony_ci pn_object(oph->pn_sdev, 0x00), 34662306a36Sopenharmony_ci pn_object(oph->pn_rdev, oph->pn_robj), 34762306a36Sopenharmony_ci PN_COMMGR); 34862306a36Sopenharmony_ci} 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci/* packet type functions */ 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci/* 35462306a36Sopenharmony_ci * Stuff received packets to associated sockets. 35562306a36Sopenharmony_ci * On error, returns non-zero and releases the skb. 35662306a36Sopenharmony_ci */ 35762306a36Sopenharmony_cistatic int phonet_rcv(struct sk_buff *skb, struct net_device *dev, 35862306a36Sopenharmony_ci struct packet_type *pkttype, 35962306a36Sopenharmony_ci struct net_device *orig_dev) 36062306a36Sopenharmony_ci{ 36162306a36Sopenharmony_ci struct net *net = dev_net(dev); 36262306a36Sopenharmony_ci struct phonethdr *ph; 36362306a36Sopenharmony_ci struct sockaddr_pn sa; 36462306a36Sopenharmony_ci u16 len; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci skb = skb_share_check(skb, GFP_ATOMIC); 36762306a36Sopenharmony_ci if (!skb) 36862306a36Sopenharmony_ci return NET_RX_DROP; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci /* check we have at least a full Phonet header */ 37162306a36Sopenharmony_ci if (!pskb_pull(skb, sizeof(struct phonethdr))) 37262306a36Sopenharmony_ci goto out; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci /* check that the advertised length is correct */ 37562306a36Sopenharmony_ci ph = pn_hdr(skb); 37662306a36Sopenharmony_ci len = get_unaligned_be16(&ph->pn_length); 37762306a36Sopenharmony_ci if (len < 2) 37862306a36Sopenharmony_ci goto out; 37962306a36Sopenharmony_ci len -= 2; 38062306a36Sopenharmony_ci if ((len > skb->len) || pskb_trim(skb, len)) 38162306a36Sopenharmony_ci goto out; 38262306a36Sopenharmony_ci skb_reset_transport_header(skb); 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci pn_skb_get_dst_sockaddr(skb, &sa); 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci /* check if this is broadcasted */ 38762306a36Sopenharmony_ci if (pn_sockaddr_get_addr(&sa) == PNADDR_BROADCAST) { 38862306a36Sopenharmony_ci pn_deliver_sock_broadcast(net, skb); 38962306a36Sopenharmony_ci goto out; 39062306a36Sopenharmony_ci } 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci /* resource routing */ 39362306a36Sopenharmony_ci if (pn_sockaddr_get_object(&sa) == 0) { 39462306a36Sopenharmony_ci struct sock *sk = pn_find_sock_by_res(net, sa.spn_resource); 39562306a36Sopenharmony_ci if (sk) 39662306a36Sopenharmony_ci return sk_receive_skb(sk, skb, 0); 39762306a36Sopenharmony_ci } 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci /* check if we are the destination */ 40062306a36Sopenharmony_ci if (phonet_address_lookup(net, pn_sockaddr_get_addr(&sa)) == 0) { 40162306a36Sopenharmony_ci /* Phonet packet input */ 40262306a36Sopenharmony_ci struct sock *sk = pn_find_sock_by_sa(net, &sa); 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci if (sk) 40562306a36Sopenharmony_ci return sk_receive_skb(sk, skb, 0); 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci if (can_respond(skb)) { 40862306a36Sopenharmony_ci send_obj_unreachable(skb); 40962306a36Sopenharmony_ci send_reset_indications(skb); 41062306a36Sopenharmony_ci } 41162306a36Sopenharmony_ci } else if (unlikely(skb->pkt_type == PACKET_LOOPBACK)) 41262306a36Sopenharmony_ci goto out; /* Race between address deletion and loopback */ 41362306a36Sopenharmony_ci else { 41462306a36Sopenharmony_ci /* Phonet packet routing */ 41562306a36Sopenharmony_ci struct net_device *out_dev; 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci out_dev = phonet_route_output(net, pn_sockaddr_get_addr(&sa)); 41862306a36Sopenharmony_ci if (!out_dev) { 41962306a36Sopenharmony_ci net_dbg_ratelimited("No Phonet route to %02X\n", 42062306a36Sopenharmony_ci pn_sockaddr_get_addr(&sa)); 42162306a36Sopenharmony_ci goto out; 42262306a36Sopenharmony_ci } 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci __skb_push(skb, sizeof(struct phonethdr)); 42562306a36Sopenharmony_ci skb->dev = out_dev; 42662306a36Sopenharmony_ci if (out_dev == dev) { 42762306a36Sopenharmony_ci net_dbg_ratelimited("Phonet loop to %02X on %s\n", 42862306a36Sopenharmony_ci pn_sockaddr_get_addr(&sa), 42962306a36Sopenharmony_ci dev->name); 43062306a36Sopenharmony_ci goto out_dev; 43162306a36Sopenharmony_ci } 43262306a36Sopenharmony_ci /* Some drivers (e.g. TUN) do not allocate HW header space */ 43362306a36Sopenharmony_ci if (skb_cow_head(skb, out_dev->hard_header_len)) 43462306a36Sopenharmony_ci goto out_dev; 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci if (dev_hard_header(skb, out_dev, ETH_P_PHONET, NULL, NULL, 43762306a36Sopenharmony_ci skb->len) < 0) 43862306a36Sopenharmony_ci goto out_dev; 43962306a36Sopenharmony_ci dev_queue_xmit(skb); 44062306a36Sopenharmony_ci dev_put(out_dev); 44162306a36Sopenharmony_ci return NET_RX_SUCCESS; 44262306a36Sopenharmony_ciout_dev: 44362306a36Sopenharmony_ci dev_put(out_dev); 44462306a36Sopenharmony_ci } 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ciout: 44762306a36Sopenharmony_ci kfree_skb(skb); 44862306a36Sopenharmony_ci return NET_RX_DROP; 44962306a36Sopenharmony_ci} 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_cistatic struct packet_type phonet_packet_type __read_mostly = { 45262306a36Sopenharmony_ci .type = cpu_to_be16(ETH_P_PHONET), 45362306a36Sopenharmony_ci .func = phonet_rcv, 45462306a36Sopenharmony_ci}; 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_cistatic DEFINE_MUTEX(proto_tab_lock); 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ciint __init_or_module phonet_proto_register(unsigned int protocol, 45962306a36Sopenharmony_ci const struct phonet_protocol *pp) 46062306a36Sopenharmony_ci{ 46162306a36Sopenharmony_ci int err = 0; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci if (protocol >= PHONET_NPROTO) 46462306a36Sopenharmony_ci return -EINVAL; 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci err = proto_register(pp->prot, 1); 46762306a36Sopenharmony_ci if (err) 46862306a36Sopenharmony_ci return err; 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci mutex_lock(&proto_tab_lock); 47162306a36Sopenharmony_ci if (proto_tab[protocol]) 47262306a36Sopenharmony_ci err = -EBUSY; 47362306a36Sopenharmony_ci else 47462306a36Sopenharmony_ci rcu_assign_pointer(proto_tab[protocol], pp); 47562306a36Sopenharmony_ci mutex_unlock(&proto_tab_lock); 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci return err; 47862306a36Sopenharmony_ci} 47962306a36Sopenharmony_ciEXPORT_SYMBOL(phonet_proto_register); 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_civoid phonet_proto_unregister(unsigned int protocol, 48262306a36Sopenharmony_ci const struct phonet_protocol *pp) 48362306a36Sopenharmony_ci{ 48462306a36Sopenharmony_ci mutex_lock(&proto_tab_lock); 48562306a36Sopenharmony_ci BUG_ON(proto_tab[protocol] != pp); 48662306a36Sopenharmony_ci RCU_INIT_POINTER(proto_tab[protocol], NULL); 48762306a36Sopenharmony_ci mutex_unlock(&proto_tab_lock); 48862306a36Sopenharmony_ci synchronize_rcu(); 48962306a36Sopenharmony_ci proto_unregister(pp->prot); 49062306a36Sopenharmony_ci} 49162306a36Sopenharmony_ciEXPORT_SYMBOL(phonet_proto_unregister); 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci/* Module registration */ 49462306a36Sopenharmony_cistatic int __init phonet_init(void) 49562306a36Sopenharmony_ci{ 49662306a36Sopenharmony_ci int err; 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci err = phonet_device_init(); 49962306a36Sopenharmony_ci if (err) 50062306a36Sopenharmony_ci return err; 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci pn_sock_init(); 50362306a36Sopenharmony_ci err = sock_register(&phonet_proto_family); 50462306a36Sopenharmony_ci if (err) { 50562306a36Sopenharmony_ci printk(KERN_ALERT 50662306a36Sopenharmony_ci "phonet protocol family initialization failed\n"); 50762306a36Sopenharmony_ci goto err_sock; 50862306a36Sopenharmony_ci } 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci dev_add_pack(&phonet_packet_type); 51162306a36Sopenharmony_ci phonet_sysctl_init(); 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci err = isi_register(); 51462306a36Sopenharmony_ci if (err) 51562306a36Sopenharmony_ci goto err; 51662306a36Sopenharmony_ci return 0; 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_cierr: 51962306a36Sopenharmony_ci phonet_sysctl_exit(); 52062306a36Sopenharmony_ci sock_unregister(PF_PHONET); 52162306a36Sopenharmony_ci dev_remove_pack(&phonet_packet_type); 52262306a36Sopenharmony_cierr_sock: 52362306a36Sopenharmony_ci phonet_device_exit(); 52462306a36Sopenharmony_ci return err; 52562306a36Sopenharmony_ci} 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_cistatic void __exit phonet_exit(void) 52862306a36Sopenharmony_ci{ 52962306a36Sopenharmony_ci isi_unregister(); 53062306a36Sopenharmony_ci phonet_sysctl_exit(); 53162306a36Sopenharmony_ci sock_unregister(PF_PHONET); 53262306a36Sopenharmony_ci dev_remove_pack(&phonet_packet_type); 53362306a36Sopenharmony_ci phonet_device_exit(); 53462306a36Sopenharmony_ci} 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_cimodule_init(phonet_init); 53762306a36Sopenharmony_cimodule_exit(phonet_exit); 53862306a36Sopenharmony_ciMODULE_DESCRIPTION("Phonet protocol stack for Linux"); 53962306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 54062306a36Sopenharmony_ciMODULE_ALIAS_NETPROTO(PF_PHONET); 541