Commit 2ba4d5c2 authored by Jukka Rissanen's avatar Jukka Rissanen Committed by Jukka Rissanen
Browse files

net: socket: packet: Add support to SOCK_DGRAM packet sockets



Allow user to create SOCK_DGRAM type AF_PACKET socket. This
allows user to send raw IP packets without specifying
L2 (like Ethernet) headers.

Signed-off-by: default avatarJukka Rissanen <jukka.rissanen@linux.intel.com>
parent 3d8bae81
Loading
Loading
Loading
Loading
+40 −13
Original line number Diff line number Diff line
@@ -512,6 +512,7 @@ enum net_verdict net_conn_input(struct net_pkt *pkt,
	struct net_if *pkt_iface = net_pkt_iface(pkt);
	struct net_conn *best_match = NULL;
	bool is_mcast_pkt = false, mcast_pkt_delivered = false;
	bool raw_pkt_delivered = false;
	int16_t best_rank = -1;
	struct net_conn *conn;
	uint16_t src_port;
@@ -524,7 +525,9 @@ enum net_verdict net_conn_input(struct net_pkt *pkt,
		src_port = proto_hdr->tcp->src_port;
		dst_port = proto_hdr->tcp->dst_port;
	} else if (IS_ENABLED(CONFIG_NET_SOCKETS_PACKET)) {
		if (net_pkt_family(pkt) != AF_PACKET || proto != ETH_P_ALL) {
		if (net_pkt_family(pkt) != AF_PACKET ||
		    (!IS_ENABLED(CONFIG_NET_SOCKETS_PACKET_DGRAM) &&
		     proto != ETH_P_ALL)) {
			return NET_DROP;
		}

@@ -569,9 +572,20 @@ enum net_verdict net_conn_input(struct net_pkt *pkt,
	}

	SYS_SLIST_FOR_EACH_CONTAINER(&conn_used, conn, node) {
		if (conn->proto != proto) {
		/* For packet socket data, the proto is set to ETH_P_ALL but
		 * the listener might have a specific protocol set. This is ok
		 * and let the packet pass this check in this case.
		 */
		if (IS_ENABLED(CONFIG_NET_SOCKETS_PACKET_DGRAM) ||
		    IS_ENABLED(CONFIG_NET_SOCKETS_PACKET)) {
			if ((conn->proto != proto) && (proto != ETH_P_ALL)) {
				continue;
			}
		} else {
			if ((conn->proto != proto)) {
				continue;
			}
		}

		if (conn->family != AF_UNSPEC &&
		    conn->family != net_pkt_family(pkt)) {
@@ -660,6 +674,7 @@ enum net_verdict net_conn_input(struct net_pkt *pkt,
		} else if (IS_ENABLED(CONFIG_NET_SOCKETS_PACKET)) {
			if (conn->flags & NET_CONN_LOCAL_ADDR_SET) {
				struct sockaddr_ll *local;
				struct net_pkt *raw_pkt;

				local = (struct sockaddr_ll *)&conn->local_addr;

@@ -668,15 +683,26 @@ enum net_verdict net_conn_input(struct net_pkt *pkt,
					continue;
				}

				if (best_rank < NET_CONN_RANK(conn->flags)) {
					best_rank = NET_CONN_RANK(conn->flags);
					best_match = conn;
				NET_DBG("[%p] raw match found cb %p ud %p",
					conn, conn->cb,	conn->user_data);

				raw_pkt = net_pkt_clone(pkt, CLONE_TIMEOUT);
				if (!raw_pkt) {
					goto drop;
				}

				if (conn->cb(conn, raw_pkt, ip_hdr,
					     proto_hdr, conn->user_data) ==
								NET_DROP) {
					net_stats_update_per_proto_drop(
							pkt_iface, proto);
					net_pkt_unref(raw_pkt);
				} else {
				if (best_rank < NET_CONN_RANK(conn->flags)) {
					best_rank = 0;
					best_match = conn;
					net_stats_update_per_proto_recv(
						pkt_iface, proto);
				}

				raw_pkt_delivered = true;
			}
		} else if (IS_ENABLED(CONFIG_NET_SOCKETS_CAN)) {
			best_rank = 0;
@@ -684,9 +710,10 @@ enum net_verdict net_conn_input(struct net_pkt *pkt,
		}
	}

	if (is_mcast_pkt && mcast_pkt_delivered) {
		/* As one or more multicast packets have already been delivered
		 * in the loop above, we shall not call the callback again here
	if ((is_mcast_pkt && mcast_pkt_delivered) || raw_pkt_delivered) {
		/* As one or more multicast or raw socket packets have already
		 * been delivered in the loop above, we shall not call the
		 * callback again here.
		 */

		net_pkt_unref(pkt);
+32 −7
Original line number Diff line number Diff line
@@ -166,7 +166,11 @@ int net_context_get(sa_family_t family,
				}
			}
		} else {
			if (family == AF_PACKET || family == AF_CAN) {
			if (IS_ENABLED(CONFIG_NET_SOCKETS_PACKET) &&
			    family == AF_PACKET && type == SOCK_DGRAM &&
			    ip_proto > 0) {
				goto check_context;
			} else if (family == AF_PACKET || family == AF_CAN) {
				NET_DBG("Invalid family");
				return -EPROTOTYPE;
			}
@@ -221,6 +225,7 @@ int net_context_get(sa_family_t family,
			return -EOPNOTSUPP;
		}

	check_context:
		if (!context) {
			NET_DBG("Invalid context");
			return -EINVAL;
@@ -443,7 +448,7 @@ static int bind_default(struct net_context *context)
		}

		ll_addr.sll_family = AF_PACKET;
		ll_addr.sll_protocol = ETH_P_ALL;
		ll_addr.sll_protocol = htons(ETH_P_ALL);
		ll_addr.sll_ifindex = net_if_get_by_iface(net_if_get_default());

		return net_context_bind(context, (struct sockaddr *)&ll_addr,
@@ -714,13 +719,15 @@ int net_context_bind(struct net_context *context, const struct sockaddr *addr,
			ll_addr->sll_protocol;
		net_sll_ptr(&context->local)->sll_addr =
			net_if_get_link_addr(iface)->addr;
		net_sll_ptr(&context->local)->sll_halen =
			net_if_get_link_addr(iface)->len;

		NET_DBG("Context %p binding to %d iface[%d] %p addr %s",
			context, net_context_get_ip_proto(context),
		NET_DBG("Context %p bind to type 0x%04x iface[%d] %p addr %s",
			context, htons(net_context_get_ip_proto(context)),
			ll_addr->sll_ifindex, iface,
			log_strdup(net_sprint_ll_addr(
					   net_if_get_link_addr(iface)->addr,
					   net_if_get_link_addr(iface)->len)));
				net_sll_ptr(&context->local)->sll_addr,
				net_sll_ptr(&context->local)->sll_halen)));

		return 0;
	}
@@ -1454,6 +1461,21 @@ static int context_sendto(struct net_context *context,
				ll_addr->sll_ifindex);
			return -EDESTADDRREQ;
		}

		if (net_context_get_type(context) == SOCK_DGRAM) {
			context->flags |= NET_CONTEXT_REMOTE_ADDR_SET;

			/* The user must set the protocol in send call */

			/* For sendmsg() call, we might have set ll_addr to
			 * point to remote addr.
			 */
			if ((void *)&context->remote != (void *)ll_addr) {
				memcpy((struct sockaddr_ll *)&context->remote,
				       ll_addr, sizeof(struct sockaddr_ll));
			}
		}

	} else if (IS_ENABLED(CONFIG_NET_SOCKETS_CAN) &&
		   net_context_get_family(context) == AF_CAN) {
		struct sockaddr_can *can_addr = (struct sockaddr_can *)dst_addr;
@@ -1935,9 +1957,12 @@ int net_context_recv(struct net_context *context,
				net_sll_ptr(&context->local)->sll_ifindex;
			addr.sll_protocol =
				net_sll_ptr(&context->local)->sll_protocol;
			addr.sll_halen =
				net_sll_ptr(&context->local)->sll_halen;

			memcpy(addr.sll_addr,
			       net_sll_ptr(&context->local)->sll_addr,
			       sizeof(addr.sll_addr));
			       MIN(addr.sll_halen, sizeof(addr.sll_addr)));

			ret = recv_raw(context, cb, timeout,
				       (struct sockaddr *)&addr, user_data);
+22 −1
Original line number Diff line number Diff line
@@ -594,7 +594,28 @@ static int ethernet_send(struct net_if *iface, struct net_pkt *pkt)
		ptype = htons(NET_ETH_PTYPE_IPV6);
	} else if (IS_ENABLED(CONFIG_NET_SOCKETS_PACKET) &&
		   net_pkt_family(pkt) == AF_PACKET) {
		struct net_context *context = net_pkt_context(pkt);

		if (context && net_context_get_type(context) == SOCK_DGRAM) {
			struct sockaddr_ll *dst_addr;
			struct sockaddr_ll_ptr *src_addr;

			/* The destination address is set in remote for this
			 * socket type.
			 */
			dst_addr = (struct sockaddr_ll *)&context->remote;
			src_addr = (struct sockaddr_ll_ptr *)&context->local;

			net_pkt_lladdr_dst(pkt)->addr = dst_addr->sll_addr;
			net_pkt_lladdr_dst(pkt)->len =
						sizeof(struct net_eth_addr);
			net_pkt_lladdr_src(pkt)->addr = src_addr->sll_addr;
			net_pkt_lladdr_src(pkt)->len =
						sizeof(struct net_eth_addr);
			ptype = dst_addr->sll_protocol;
		} else {
			goto send;
		}
	} else if (IS_ENABLED(CONFIG_NET_GPTP) && net_pkt_is_gptp(pkt)) {
		ptype = htons(NET_ETH_PTYPE_PTP);
	} else if (IS_ENABLED(CONFIG_NET_LLDP) && net_pkt_is_lldp(pkt)) {
+11 −0
Original line number Diff line number Diff line
@@ -124,6 +124,17 @@ config NET_SOCKETS_PACKET
	  while sending. While receiving, packets (including all the headers)
	  will be feed to sockets as it as from the driver.

config NET_SOCKETS_PACKET_DGRAM
	bool "Enable packet socket SOCK_DGRAM support"
	depends on NET_SOCKETS_PACKET
	default y
	help
	  For AF_PACKET sockets with SOCK_DGRAM type, the L2 header
	  is removed before the packet is passed to the user.  Packets sent
	  through a SOCK_DGRAM packet socket get a suitable L2 header based
	  on the information in the sockaddr_ll destination address before
	  they are queued.

config NET_SOCKETS_CAN
	bool "Enable socket CAN support [EXPERIMENTAL]"
	select NET_L2_CANBUS_RAW
+4 −3
Original line number Diff line number Diff line
@@ -372,11 +372,12 @@ static const struct socket_op_vtable packet_sock_fd_op_vtable = {

static bool packet_is_supported(int family, int type, int proto)
{
	if (type != SOCK_RAW || proto != ETH_P_ALL) {
		return false;
	if (((type == SOCK_RAW) && (proto == ETH_P_ALL)) ||
	    ((type == SOCK_DGRAM) && (proto > 0))) {
		return true;
	}

	return true;
	return false;
}

NET_SOCKET_REGISTER(af_packet, AF_PACKET, packet_is_supported, zpacket_socket);