Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1c32c5ad authored by Herbert Xu's avatar Herbert Xu Committed by David S. Miller
Browse files

inet: Add ip_make_skb and ip_finish_skb



This patch adds the helper ip_make_skb which is like ip_append_data
and ip_push_pending_frames all rolled into one, except that it does
not send the skb produced.  The sending part is carried out by
ip_send_skb, which the transport protocol can call after it has
tweaked the skb.

It is meant to be called in cases where corking is not used should
have a one-to-one correspondence to sendmsg.

This patch also adds the helper ip_finish_skb which is meant to
be replace ip_push_pending_frames when corking is required.
Previously the protocol stack would peek at the socket write
queue and add its header to the first packet.  With ip_finish_skb,
the protocol stack can directly operate on the final skb instead,
just like the non-corking case with ip_make_skb.

Signed-off-by: default avatarHerbert Xu <herbert@gondor.apana.org.au>
Acked-by: default avatarEric Dumazet <eric.dumazet@gmail.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 1470ddf7
Loading
Loading
Loading
Loading
+16 −0
Original line number Diff line number Diff line
@@ -116,8 +116,24 @@ extern int ip_append_data(struct sock *sk,
extern int		ip_generic_getfrag(void *from, char *to, int offset, int len, int odd, struct sk_buff *skb);
extern ssize_t		ip_append_page(struct sock *sk, struct page *page,
				int offset, size_t size, int flags);
extern struct sk_buff  *__ip_make_skb(struct sock *sk,
				      struct sk_buff_head *queue,
				      struct inet_cork *cork);
extern int		ip_send_skb(struct sk_buff *skb);
extern int		ip_push_pending_frames(struct sock *sk);
extern void		ip_flush_pending_frames(struct sock *sk);
extern struct sk_buff  *ip_make_skb(struct sock *sk,
				    int getfrag(void *from, char *to, int offset, int len,
						int odd, struct sk_buff *skb),
				    void *from, int length, int transhdrlen,
				    struct ipcm_cookie *ipc,
				    struct rtable **rtp,
				    unsigned int flags);

static inline struct sk_buff *ip_finish_skb(struct sock *sk)
{
	return __ip_make_skb(sk, &sk->sk_write_queue, &inet_sk(sk)->cork);
}

/* datagram.c */
extern int		ip4_datagram_connect(struct sock *sk, 
+51 −14
Original line number Diff line number Diff line
@@ -1267,7 +1267,7 @@ static void ip_cork_release(struct inet_cork *cork)
 *	Combined all pending IP fragments on the socket as one IP datagram
 *	and push them out.
 */
static int __ip_push_pending_frames(struct sock *sk,
struct sk_buff *__ip_make_skb(struct sock *sk,
			      struct sk_buff_head *queue,
			      struct inet_cork *cork)
{
@@ -1280,7 +1280,6 @@ static int __ip_push_pending_frames(struct sock *sk,
	struct iphdr *iph;
	__be16 df = 0;
	__u8 ttl;
	int err = 0;

	if ((skb = __skb_dequeue(queue)) == NULL)
		goto out;
@@ -1351,28 +1350,37 @@ static int __ip_push_pending_frames(struct sock *sk,
		icmp_out_count(net, ((struct icmphdr *)
			skb_transport_header(skb))->type);

	/* Netfilter gets whole the not fragmented skb. */
	ip_cork_release(cork);
out:
	return skb;
}

int ip_send_skb(struct sk_buff *skb)
{
	struct net *net = sock_net(skb->sk);
	int err;

	err = ip_local_out(skb);
	if (err) {
		if (err > 0)
			err = net_xmit_errno(err);
		if (err)
			goto error;
			IP_INC_STATS(net, IPSTATS_MIB_OUTDISCARDS);
	}

out:
	ip_cork_release(cork);
	return err;

error:
	IP_INC_STATS(net, IPSTATS_MIB_OUTDISCARDS);
	goto out;
}

int ip_push_pending_frames(struct sock *sk)
{
	return __ip_push_pending_frames(sk, &sk->sk_write_queue,
					&inet_sk(sk)->cork);
	struct sk_buff *skb;

	skb = ip_finish_skb(sk);
	if (!skb)
		return 0;

	/* Netfilter gets whole the not fragmented skb. */
	return ip_send_skb(skb);
}

/*
@@ -1395,6 +1403,35 @@ void ip_flush_pending_frames(struct sock *sk)
	__ip_flush_pending_frames(sk, &sk->sk_write_queue, &inet_sk(sk)->cork);
}

struct sk_buff *ip_make_skb(struct sock *sk,
			    int getfrag(void *from, char *to, int offset,
					int len, int odd, struct sk_buff *skb),
			    void *from, int length, int transhdrlen,
			    struct ipcm_cookie *ipc, struct rtable **rtp,
			    unsigned int flags)
{
	struct inet_cork cork = {};
	struct sk_buff_head queue;
	int err;

	if (flags & MSG_PROBE)
		return NULL;

	__skb_queue_head_init(&queue);

	err = ip_setup_cork(sk, &cork, ipc, rtp);
	if (err)
		return ERR_PTR(err);

	err = __ip_append_data(sk, &queue, &cork, getfrag,
			       from, length, transhdrlen, flags);
	if (err) {
		__ip_flush_pending_frames(sk, &queue, &cork);
		return ERR_PTR(err);
	}

	return __ip_make_skb(sk, &queue, &cork);
}

/*
 *	Fetch data from kernel space and fill in checksum if needed.