aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv6
diff options
context:
space:
mode:
authorVlad Yasevich <vyasevich@gmail.com>2015-01-31 10:40:15 -0500
committerDavid S. Miller <davem@davemloft.net>2015-02-02 22:28:04 -0500
commit6422398c2ab09268a55112f98cbf96bbf0184328 (patch)
tree1a3bdd60945346bbdbe6e017807d6a8c78889110 /net/ipv6
parent0bbe84a67b0b5460c2e67101b3aa95cb828845f3 (diff)
ipv6: introduce ipv6_make_skb
This commit is very similar to commit 1c32c5ad6fac8cee1a77449f5abf211e911ff830 Author: Herbert Xu <herbert@gondor.apana.org.au> Date: Tue Mar 1 02:36:47 2011 +0000 inet: Add ip_make_skb and ip_finish_skb It adds IPv6 version of the helpers ip6_make_skb and ip6_finish_skb. The job of ip6_make_skb is to collect messages into an ipv6 packet and poplulate ipv6 eader. The job of ip6_finish_skb is to transmit the generated skb. Together they replicated the job of ip6_push_pending_frames() while also provide the capability to be called independently. This will be needed to add lockless UDP sendmsg support. Signed-off-by: Vladislav Yasevich <vyasevic@redhat.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv6')
-rw-r--r--net/ipv6/ip6_output.c103
1 files changed, 84 insertions, 19 deletions
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
index 1b66453a695d..b89d3c27dac7 100644
--- a/net/ipv6/ip6_output.c
+++ b/net/ipv6/ip6_output.c
@@ -1564,22 +1564,23 @@ static void ip6_cork_release(struct inet_cork_full *cork,
1564 memset(&cork->fl, 0, sizeof(cork->fl)); 1564 memset(&cork->fl, 0, sizeof(cork->fl));
1565} 1565}
1566 1566
1567int ip6_push_pending_frames(struct sock *sk) 1567struct sk_buff *__ip6_make_skb(struct sock *sk,
1568 struct sk_buff_head *queue,
1569 struct inet_cork_full *cork,
1570 struct inet6_cork *v6_cork)
1568{ 1571{
1569 struct sk_buff *skb, *tmp_skb; 1572 struct sk_buff *skb, *tmp_skb;
1570 struct sk_buff **tail_skb; 1573 struct sk_buff **tail_skb;
1571 struct in6_addr final_dst_buf, *final_dst = &final_dst_buf; 1574 struct in6_addr final_dst_buf, *final_dst = &final_dst_buf;
1572 struct inet_sock *inet = inet_sk(sk);
1573 struct ipv6_pinfo *np = inet6_sk(sk); 1575 struct ipv6_pinfo *np = inet6_sk(sk);
1574 struct net *net = sock_net(sk); 1576 struct net *net = sock_net(sk);
1575 struct ipv6hdr *hdr; 1577 struct ipv6hdr *hdr;
1576 struct ipv6_txoptions *opt = np->cork.opt; 1578 struct ipv6_txoptions *opt = v6_cork->opt;
1577 struct rt6_info *rt = (struct rt6_info *)inet->cork.base.dst; 1579 struct rt6_info *rt = (struct rt6_info *)cork->base.dst;
1578 struct flowi6 *fl6 = &inet->cork.fl.u.ip6; 1580 struct flowi6 *fl6 = &cork->fl.u.ip6;
1579 unsigned char proto = fl6->flowi6_proto; 1581 unsigned char proto = fl6->flowi6_proto;
1580 int err = 0;
1581 1582
1582 skb = __skb_dequeue(&sk->sk_write_queue); 1583 skb = __skb_dequeue(queue);
1583 if (skb == NULL) 1584 if (skb == NULL)
1584 goto out; 1585 goto out;
1585 tail_skb = &(skb_shinfo(skb)->frag_list); 1586 tail_skb = &(skb_shinfo(skb)->frag_list);
@@ -1587,7 +1588,7 @@ int ip6_push_pending_frames(struct sock *sk)
1587 /* move skb->data to ip header from ext header */ 1588 /* move skb->data to ip header from ext header */
1588 if (skb->data < skb_network_header(skb)) 1589 if (skb->data < skb_network_header(skb))
1589 __skb_pull(skb, skb_network_offset(skb)); 1590 __skb_pull(skb, skb_network_offset(skb));
1590 while ((tmp_skb = __skb_dequeue(&sk->sk_write_queue)) != NULL) { 1591 while ((tmp_skb = __skb_dequeue(queue)) != NULL) {
1591 __skb_pull(tmp_skb, skb_network_header_len(skb)); 1592 __skb_pull(tmp_skb, skb_network_header_len(skb));
1592 *tail_skb = tmp_skb; 1593 *tail_skb = tmp_skb;
1593 tail_skb = &(tmp_skb->next); 1594 tail_skb = &(tmp_skb->next);
@@ -1612,10 +1613,10 @@ int ip6_push_pending_frames(struct sock *sk)
1612 skb_reset_network_header(skb); 1613 skb_reset_network_header(skb);
1613 hdr = ipv6_hdr(skb); 1614 hdr = ipv6_hdr(skb);
1614 1615
1615 ip6_flow_hdr(hdr, np->cork.tclass, 1616 ip6_flow_hdr(hdr, v6_cork->tclass,
1616 ip6_make_flowlabel(net, skb, fl6->flowlabel, 1617 ip6_make_flowlabel(net, skb, fl6->flowlabel,
1617 np->autoflowlabel)); 1618 np->autoflowlabel));
1618 hdr->hop_limit = np->cork.hop_limit; 1619 hdr->hop_limit = v6_cork->hop_limit;
1619 hdr->nexthdr = proto; 1620 hdr->nexthdr = proto;
1620 hdr->saddr = fl6->saddr; 1621 hdr->saddr = fl6->saddr;
1621 hdr->daddr = *final_dst; 1622 hdr->daddr = *final_dst;
@@ -1632,25 +1633,45 @@ int ip6_push_pending_frames(struct sock *sk)
1632 ICMP6_INC_STATS(net, idev, ICMP6_MIB_OUTMSGS); 1633 ICMP6_INC_STATS(net, idev, ICMP6_MIB_OUTMSGS);
1633 } 1634 }
1634 1635
1636 ip6_cork_release(cork, v6_cork);
1637out:
1638 return skb;
1639}
1640
1641int ip6_send_skb(struct sk_buff *skb)
1642{
1643 struct net *net = sock_net(skb->sk);
1644 struct rt6_info *rt = (struct rt6_info *)skb_dst(skb);
1645 int err;
1646
1635 err = ip6_local_out(skb); 1647 err = ip6_local_out(skb);
1636 if (err) { 1648 if (err) {
1637 if (err > 0) 1649 if (err > 0)
1638 err = net_xmit_errno(err); 1650 err = net_xmit_errno(err);
1639 if (err) 1651 if (err)
1640 goto error; 1652 IP6_INC_STATS(net, rt->rt6i_idev,
1653 IPSTATS_MIB_OUTDISCARDS);
1641 } 1654 }
1642 1655
1643out:
1644 ip6_cork_release(&inet->cork, &np->cork);
1645 return err; 1656 return err;
1646error: 1657}
1647 IP6_INC_STATS(net, rt->rt6i_idev, IPSTATS_MIB_OUTDISCARDS); 1658
1648 goto out; 1659int ip6_push_pending_frames(struct sock *sk)
1660{
1661 struct sk_buff *skb;
1662
1663 skb = ip6_finish_skb(sk);
1664 if (!skb)
1665 return 0;
1666
1667 return ip6_send_skb(skb);
1649} 1668}
1650EXPORT_SYMBOL_GPL(ip6_push_pending_frames); 1669EXPORT_SYMBOL_GPL(ip6_push_pending_frames);
1651 1670
1652static void __ip6_flush_pending_frames(struct sock *sk, 1671static void __ip6_flush_pending_frames(struct sock *sk,
1653 struct sk_buff_head *queue) 1672 struct sk_buff_head *queue,
1673 struct inet_cork_full *cork,
1674 struct inet6_cork *v6_cork)
1654{ 1675{
1655 struct sk_buff *skb; 1676 struct sk_buff *skb;
1656 1677
@@ -1661,11 +1682,55 @@ static void __ip6_flush_pending_frames(struct sock *sk,
1661 kfree_skb(skb); 1682 kfree_skb(skb);
1662 } 1683 }
1663 1684
1664 ip6_cork_release(&inet_sk(sk)->cork, &inet6_sk(sk)->cork); 1685 ip6_cork_release(cork, v6_cork);
1665} 1686}
1666 1687
1667void ip6_flush_pending_frames(struct sock *sk) 1688void ip6_flush_pending_frames(struct sock *sk)
1668{ 1689{
1669 __ip6_flush_pending_frames(sk, &sk->sk_write_queue); 1690 __ip6_flush_pending_frames(sk, &sk->sk_write_queue,
1691 &inet_sk(sk)->cork, &inet6_sk(sk)->cork);
1670} 1692}
1671EXPORT_SYMBOL_GPL(ip6_flush_pending_frames); 1693EXPORT_SYMBOL_GPL(ip6_flush_pending_frames);
1694
1695struct sk_buff *ip6_make_skb(struct sock *sk,
1696 int getfrag(void *from, char *to, int offset,
1697 int len, int odd, struct sk_buff *skb),
1698 void *from, int length, int transhdrlen,
1699 int hlimit, int tclass,
1700 struct ipv6_txoptions *opt, struct flowi6 *fl6,
1701 struct rt6_info *rt, unsigned int flags,
1702 int dontfrag)
1703{
1704 struct inet_cork_full cork;
1705 struct inet6_cork v6_cork;
1706 struct sk_buff_head queue;
1707 int exthdrlen = (opt ? opt->opt_flen : 0);
1708 int err;
1709
1710 if (flags & MSG_PROBE)
1711 return NULL;
1712
1713 __skb_queue_head_init(&queue);
1714
1715 cork.base.flags = 0;
1716 cork.base.addr = 0;
1717 cork.base.opt = NULL;
1718 v6_cork.opt = NULL;
1719 err = ip6_setup_cork(sk, &cork, &v6_cork, hlimit, tclass, opt, rt, fl6);
1720 if (err)
1721 return ERR_PTR(err);
1722
1723 if (dontfrag < 0)
1724 dontfrag = inet6_sk(sk)->dontfrag;
1725
1726 err = __ip6_append_data(sk, fl6, &queue, &cork.base, &v6_cork,
1727 &current->task_frag, getfrag, from,
1728 length + exthdrlen, transhdrlen + exthdrlen,
1729 flags, dontfrag);
1730 if (err) {
1731 __ip6_flush_pending_frames(sk, &queue, &cork, &v6_cork);
1732 return ERR_PTR(err);
1733 }
1734
1735 return __ip6_make_skb(sk, &queue, &cork, &v6_cork);
1736}