本文整理汇总了C++中csum_fold函数的典型用法代码示例。如果您正苦于以下问题:C++ csum_fold函数的具体用法?C++ csum_fold怎么用?C++ csum_fold使用的例子?那么恭喜您, 这里精选的函数代码示例或许可以为您提供帮助。
在下文中一共展示了csum_fold函数的20个代码示例,这些例子默认根据受欢迎程度排序。您可以为喜欢或者感觉有用的代码点赞,您的评价将有助于我们的系统推荐出更棒的C++代码示例。
示例1: tcp_fast_csum_update
static inline void
tcp_fast_csum_update(int af, struct tcphdr *tcph,
const union nf_inet_addr *oldip,
const union nf_inet_addr *newip,
__be16 oldport, __be16 newport)
{
#ifdef CONFIG_IP_VS_IPV6
if (af == AF_INET6)
tcph->check =
csum_fold(ip_vs_check_diff16(oldip->ip6, newip->ip6,
ip_vs_check_diff2(oldport,
newport,
~csum_unfold
(tcph->
check))));
else
#endif
tcph->check =
csum_fold(ip_vs_check_diff4(oldip->ip, newip->ip,
ip_vs_check_diff2(oldport,
newport,
~csum_unfold
(tcph->
check))));
}
开发者ID:Addision,项目名称:LVS,代码行数:25,代码来源:ip_vs_proto_tcp.c
示例2: rt_icmp_glue_reply_bits
static int rt_icmp_glue_reply_bits(const void *p, char *to,
unsigned int offset, unsigned int fraglen)
{
struct icmp_bxm *icmp_param = (struct icmp_bxm *)p;
struct icmphdr *icmph;
unsigned long csum;
/* TODO: add support for fragmented ICMP packets */
if (offset != 0)
return -EMSGSIZE;
csum = csum_partial_copy_nocheck((void *)&icmp_param->head, to,
icmp_param->head_len, icmp_param->csum);
csum = rtskb_copy_and_csum_bits(icmp_param->data.skb,
icmp_param->offset,
to + icmp_param->head_len,
fraglen - icmp_param->head_len,
csum);
icmph = (struct icmphdr *)to;
icmph->checksum = csum_fold(csum);
return 0;
}
开发者ID:BackupTheBerlios,项目名称:rtnet-svn,代码行数:27,代码来源:icmp.c
示例3: set_ect_ip
/* set ECT codepoint from IP header.
* return 0 in case there was no ECT codepoint
* return 1 in case ECT codepoint has been overwritten
* return < 0 in case there was error */
static int inline
set_ect_ip(struct sk_buff **pskb, struct iphdr *iph,
const struct ipt_ECN_info *einfo)
{
if ((iph->tos & IPT_ECN_IP_MASK)
!= (einfo->ip_ect & IPT_ECN_IP_MASK)) {
u_int16_t diffs[2];
/* raw socket (tcpdump) may have clone of incoming
* skb: don't disturb it --RR */
if (skb_cloned(*pskb) && !(*pskb)->sk) {
struct sk_buff *nskb = skb_copy(*pskb, GFP_ATOMIC);
if (!nskb)
return NF_DROP;
kfree_skb(*pskb);
*pskb = nskb;
iph = (*pskb)->nh.iph;
}
diffs[0] = htons(iph->tos) ^ 0xFFFF;
iph->tos = iph->tos & ~IPT_ECN_IP_MASK;
iph->tos = iph->tos | (einfo->ip_ect & IPT_ECN_IP_MASK);
diffs[1] = htons(iph->tos);
iph->check = csum_fold(csum_partial((char *)diffs,
sizeof(diffs),
iph->check^0xFFFF));
(*pskb)->nfcache |= NFC_ALTERED;
return 1;
}
return 0;
}
开发者ID:JBTech,项目名称:ralink_rt5350,代码行数:36,代码来源:ipt_ECN.c
示例4: fast_csum
static void fast_csum(struct snmp_ctx *ctx, unsigned char offset)
{
unsigned char s[12] = {0,};
int size;
if (offset & 1) {
memcpy(&s[1], &ctx->from, 4);
memcpy(&s[7], &ctx->to, 4);
s[0] = ~0;
s[1] = ~s[1];
s[2] = ~s[2];
s[3] = ~s[3];
s[4] = ~s[4];
s[5] = ~0;
size = 12;
} else {
memcpy(&s[0], &ctx->from, 4);
memcpy(&s[4], &ctx->to, 4);
s[0] = ~s[0];
s[1] = ~s[1];
s[2] = ~s[2];
s[3] = ~s[3];
size = 8;
}
*ctx->check = csum_fold(csum_partial(s, size,
~csum_unfold(*ctx->check)));
}
开发者ID:avagin,项目名称:linux,代码行数:27,代码来源:nf_nat_snmp_basic_main.c
示例5: __gre_build_header
static void __gre_build_header(struct sk_buff *skb,
int tunnel_hlen,
bool is_gre64)
{
const struct ovs_key_ipv4_tunnel *tun_key = OVS_CB(skb)->tun_key;
__be32 *options = (__be32 *)(skb_network_header(skb) + tunnel_hlen
- GRE_HEADER_SECTION);
struct gre_base_hdr *greh = (struct gre_base_hdr *) skb_transport_header(skb);
greh->protocol = htons(ETH_P_TEB);
greh->flags = 0;
/* Work backwards over the options so the checksum is last. */
if (tun_key->tun_flags & OVS_TNL_F_KEY || is_gre64) {
greh->flags |= GRE_KEY;
if (is_gre64) {
/* Set higher 32 bits to seq. */
*options = be64_get_high32(tun_key->tun_id);
options--;
greh->flags |= GRE_SEQ;
}
*options = be64_get_low32(tun_key->tun_id);
options--;
}
if (tun_key->tun_flags & OVS_TNL_F_CSUM) {
greh->flags |= GRE_CSUM;
*options = 0;
*(__sum16 *)options = csum_fold(skb_checksum(skb,
skb_transport_offset(skb),
skb->len - skb_transport_offset(skb),
0));
}
}
开发者ID:carriercomm,项目名称:openvSwitch.10,代码行数:33,代码来源:vport-gre.c
示例6: build_header
static void build_header(struct sk_buff *skb, int hdr_len, __be16 flags,
__be16 proto, __be32 key, __be32 seq)
{
struct gre_base_hdr *greh;
skb_push(skb, hdr_len);
skb_reset_transport_header(skb);
greh = (struct gre_base_hdr *)skb->data;
greh->flags = tnl_flags_to_gre_flags(flags);
greh->protocol = proto;
if (flags & (TUNNEL_KEY | TUNNEL_CSUM | TUNNEL_SEQ)) {
__be32 *ptr = (__be32 *)(((u8 *)greh) + hdr_len - 4);
if (flags & TUNNEL_SEQ) {
*ptr = seq;
ptr--;
}
if (flags & TUNNEL_KEY) {
*ptr = key;
ptr--;
}
if (flags & TUNNEL_CSUM &&
!(skb_shinfo(skb)->gso_type &
(SKB_GSO_GRE | SKB_GSO_GRE_CSUM))) {
*ptr = 0;
*(__sum16 *)ptr = csum_fold(skb_checksum(skb, 0,
skb->len, 0));
}
}
}
开发者ID:scollison,项目名称:net-next-nuse,代码行数:32,代码来源:ip_gre.c
示例7: check_checksum
static bool check_checksum(struct sk_buff *skb)
{
struct iphdr *iph = ip_hdr(skb);
struct gre_base_hdr *greh = (struct gre_base_hdr *)(iph + 1);
__sum16 csum = 0;
if (greh->flags & GRE_CSUM) {
switch (skb->ip_summed) {
case CHECKSUM_COMPLETE:
csum = csum_fold(skb->csum);
if (!csum)
break;
/* Fall through. */
case CHECKSUM_NONE:
skb->csum = 0;
csum = __skb_checksum_complete(skb);
skb->ip_summed = CHECKSUM_COMPLETE;
break;
}
}
return (csum == 0);
}
开发者ID:carriercomm,项目名称:openvSwitch.10,代码行数:25,代码来源:vport-gre.c
示例8: target
static unsigned int
target(struct sk_buff **pskb,
const struct net_device *in,
const struct net_device *out,
unsigned int hooknum,
const void *targinfo,
void *userinfo)
{
const struct ipt_tos_target_info *tosinfo = targinfo;
if (((*pskb)->nh.iph->tos & IPTOS_TOS_MASK) != tosinfo->tos) {
u_int16_t diffs[2];
if (!skb_ip_make_writable(pskb, sizeof(struct iphdr)))
return NF_DROP;
diffs[0] = htons((*pskb)->nh.iph->tos) ^ 0xFFFF;
(*pskb)->nh.iph->tos
= ((*pskb)->nh.iph->tos & IPTOS_PREC_MASK)
| tosinfo->tos;
diffs[1] = htons((*pskb)->nh.iph->tos);
(*pskb)->nh.iph->check
= csum_fold(csum_partial((char *)diffs,
sizeof(diffs),
(*pskb)->nh.iph->check
^0xFFFF));
(*pskb)->nfcache |= NFC_ALTERED;
}
return IPT_CONTINUE;
}
开发者ID:Antonio-Zhou,项目名称:Linux-2.6.11,代码行数:30,代码来源:ipt_TOS.c
示例9: skb_copy_and_csum_datagram_iovec
/**
* skb_copy_and_csum_datagram_iovec - Copy and checkum skb to user iovec.
* @skb: skbuff
* @hlen: hardware length
* @iov: io vector
*
* Caller _must_ check that skb will fit to this iovec.
*
* Returns: 0 - success.
* -EINVAL - checksum failure.
* -EFAULT - fault during copy. Beware, in this case iovec
* can be modified!
*/
int skb_copy_and_csum_datagram_iovec(struct sk_buff *skb,
int hlen, struct iovec *iov)
{
__wsum csum;
int chunk = skb->len - hlen;
/* Skip filled elements.
* Pretty silly, look at memcpy_toiovec, though 8)
*/
while (!iov->iov_len)
iov++;
if (iov->iov_len < chunk) {
if (__skb_checksum_complete(skb))
goto csum_error;
if (skb_copy_datagram_iovec(skb, hlen, iov, chunk))
goto fault;
} else {
csum = csum_partial(skb->data, hlen, skb->csum);
if (skb_copy_and_csum_datagram(skb, hlen, iov->iov_base,
chunk, &csum))
goto fault;
if (csum_fold(csum))
goto csum_error;
if (unlikely(skb->ip_summed == CHECKSUM_COMPLETE))
netdev_rx_csum_fault(skb->dev);
iov->iov_len -= chunk;
iov->iov_base += chunk;
}
return 0;
csum_error:
return -EINVAL;
fault:
return -EFAULT;
}
开发者ID:millken,项目名称:zhuxianB30,代码行数:48,代码来源:datagram.c
示例10: rtskb_copy_and_csum_dev
/***
* rtskb_copy_and_csum_dev
*/
void rtskb_copy_and_csum_dev(const struct rtskb *skb, u8 *to)
{
unsigned int csum;
unsigned int csstart;
if (skb->ip_summed == CHECKSUM_HW) {
csstart = skb->h.raw - skb->data;
if (csstart > skb->len)
BUG();
} else
csstart = skb->len;
memcpy(to, skb->data, csstart);
csum = 0;
if (csstart != skb->len)
csum = rtskb_copy_and_csum_bits(skb, csstart, to+csstart, skb->len-csstart, 0);
if (skb->ip_summed == CHECKSUM_HW) {
unsigned int csstuff = csstart + skb->csum;
*((unsigned short *)(to + csstuff)) = csum_fold(csum);
}
}
开发者ID:BackupTheBerlios,项目名称:rtnet-svn,代码行数:28,代码来源:rtskb.c
示例11: nf_ip_checksum
__sum16 nf_ip_checksum(struct sk_buff *skb, unsigned int hook,
unsigned int dataoff, u_int8_t protocol)
{
const struct iphdr *iph = ip_hdr(skb);
__sum16 csum = 0;
switch (skb->ip_summed) {
case CHECKSUM_COMPLETE:
if (hook != NF_INET_PRE_ROUTING && hook != NF_INET_LOCAL_IN)
break;
if ((protocol == 0 && !csum_fold(skb->csum)) ||
!csum_tcpudp_magic(iph->saddr, iph->daddr,
skb->len - dataoff, protocol,
skb->csum)) {
skb->ip_summed = CHECKSUM_UNNECESSARY;
break;
}
/* fall through */
case CHECKSUM_NONE:
if (protocol == 0)
skb->csum = 0;
else
skb->csum = csum_tcpudp_nofold(iph->saddr, iph->daddr,
skb->len - dataoff,
protocol, 0);
csum = __skb_checksum_complete(skb);
}
return csum;
}
开发者ID:274914765,项目名称:C,代码行数:29,代码来源:netfilter.c
示例12: ipoptstrip_tg
static unsigned int ipoptstrip_tg(struct sk_buff *skb,
const struct xt_action_param *par) {
struct ip_options *opt = &(IPCB(skb)->opt);
unsigned char *opt_ptr, *opt_end_ptr;
struct iphdr *iphdr;
const struct xt_ipoptstrip_tg_info *info;
__wsum csum32;
if (opt->optlen > 0) {
iphdr = ip_hdr(skb);
info = par->targinfo;
#ifdef DEBUG
printk("flags: %x\n", info->flags);
printk("Packet with IP options (%i bytes) from: %pI4 to: %pI4\n",
opt->optlen, &iphdr->saddr, &iphdr->daddr);
print_skb_header_offsets(skb);
#endif
if (! XT_IPOPTSTRIP_IS_SET(info->flags, XT_IPOPTSTRIP_KEEP_DST)) {
opt_ptr = (unsigned char*) &iphdr[1];
opt_end_ptr = opt_ptr + opt->optlen;
for (; opt_ptr < opt_end_ptr; opt_ptr++) {
switch (*opt_ptr) {
case IPOPT_LSRR:
case IPOPT_SSRR:
/* Re-write destination field with last address */
memcpy(&iphdr->daddr, (opt_ptr+(opt_ptr[1]))-4, 4);
break;
}
}
}
/* Alter header and total lengths */
iphdr->ihl = IPV4_HL; // 5 32-bit words in IPv4 header with no options
iphdr->tot_len -= cpu_to_be16(opt->optlen);
/* Move transport header pointer to after network header */
skb_set_transport_header(skb, IPV4_LEN);
/* Move remaining data up the buffer */
memmove(skb_transport_header(skb), skb_transport_header(skb) + opt->optlen,
skb->tail - (skb->transport_header + opt->optlen));
/* Remove un-needed buffer space */
skb_trim(skb, (skb->len - opt->optlen));
/* Re-calculate IP header checksum */
csum32 = csum_partial(iphdr, sizeof(struct iphdr), 0);
iphdr->check = csum_fold(csum32);
#ifdef DEBUG
print_skb_header_offsets(skb);
#endif
}
return XT_CONTINUE;
}
开发者ID:duncanje,项目名称:xt_IPOPTSTRIP,代码行数:60,代码来源:xt_IPOPTSTRIP.c
示例13: gre_build_header
void gre_build_header(struct sk_buff *skb, const struct tnl_ptk_info *tpi,
int hdr_len)
{
struct gre_base_hdr *greh;
skb_push(skb, hdr_len);
greh = (struct gre_base_hdr *)skb->data;
greh->flags = tnl_flags_to_gre_flags(tpi->flags);
greh->protocol = tpi->proto;
if (tpi->flags&(TUNNEL_KEY|TUNNEL_CSUM|TUNNEL_SEQ)) {
__be32 *ptr = (__be32 *)(((u8 *)greh) + hdr_len - 4);
if (tpi->flags&TUNNEL_SEQ) {
*ptr = tpi->seq;
ptr--;
}
if (tpi->flags&TUNNEL_KEY) {
*ptr = tpi->key;
ptr--;
}
if (tpi->flags&TUNNEL_CSUM &&
!(skb_shinfo(skb)->gso_type & SKB_GSO_GRE)) {
*ptr = 0;
*(__sum16 *)ptr = csum_fold(skb_checksum(skb, 0,
skb->len, 0));
}
}
}
开发者ID:AeroGirl,项目名称:VAR-SOM-AM33-SDK7-Kernel,代码行数:30,代码来源:gre_demux.c
示例14: set_ect_tcp
/* Return 0 if there was an error. */
static inline int
set_ect_tcp(struct sk_buff **pskb, const struct ipt_ECN_info *einfo)
{
struct tcphdr tcph;
u_int16_t diffs[2];
/* Not enought header? */
if (skb_copy_bits(*pskb, (*pskb)->nh.iph->ihl*4, &tcph, sizeof(tcph))
< 0)
return 0;
diffs[0] = ((u_int16_t *)&tcph)[6];
if (einfo->operation & IPT_ECN_OP_SET_ECE)
tcph.ece = einfo->proto.tcp.ece;
if (einfo->operation & IPT_ECN_OP_SET_CWR)
tcph.cwr = einfo->proto.tcp.cwr;
diffs[1] = ((u_int16_t *)&tcph)[6];
/* Only mangle if it's changed. */
if (diffs[0] != diffs[1]) {
diffs[0] = diffs[0] ^ 0xFFFF;
if (!skb_ip_make_writable(pskb,
(*pskb)->nh.iph->ihl*4+sizeof(tcph)))
return 0;
tcph.check = csum_fold(csum_partial((char *)diffs,
sizeof(diffs),
tcph.check^0xFFFF));
memcpy((*pskb)->data + (*pskb)->nh.iph->ihl*4,
&tcph, sizeof(tcph));
(*pskb)->nfcache |= NFC_ALTERED;
}
return 1;
}
开发者ID:xricson,项目名称:knoppix,代码行数:35,代码来源:ipt_ECN.c
示例15: icmp_glue_bits
static int icmp_glue_bits(const void *p, char *to, unsigned int offset, unsigned int fraglen)
{
struct icmp_bxm *icmp_param = (struct icmp_bxm *)p;
struct icmphdr *icmph;
unsigned long csum;
if (offset) {
icmp_param->csum=csum_partial_copy(icmp_param->data_ptr+offset-sizeof(struct icmphdr),
to, fraglen,icmp_param->csum);
return 0;
}
/*
* First fragment includes header. Note that we've done
* the other fragments first, so that we get the checksum
* for the whole packet here.
*/
csum = csum_partial_copy((void *)&icmp_param->icmph,
to, sizeof(struct icmphdr),
icmp_param->csum);
csum = csum_partial_copy(icmp_param->data_ptr,
to+sizeof(struct icmphdr),
fraglen-sizeof(struct icmphdr), csum);
icmph=(struct icmphdr *)to;
icmph->checksum = csum_fold(csum);
return 0;
}
开发者ID:AllenDowney,项目名称:clink,代码行数:27,代码来源:icmp.c
示例16: tcp_seq_csum_update
static inline void
tcp_seq_csum_update(struct tcphdr *tcph, __u32 oldseq, __u32 newseq)
{
/* do checksum later */
if (!sysctl_ip_vs_csum_offload)
tcph->check = csum_fold(ip_vs_check_diff4(oldseq, newseq,
~csum_unfold(tcph->check)));
}
开发者ID:Addision,项目名称:LVS,代码行数:8,代码来源:ip_vs_proto_tcp.c
示例17: daddr_csum_replace4
static inline void
daddr_csum_replace4(u16 *sum, u32 from, u32 to)
{
u32 diff[] = { ~from, to };
*sum = csum_fold(csum_partial((unsigned char *)diff, sizeof(diff),
~daddr_csum_unfold(*sum)));
}
开发者ID:Solinea,项目名称:l3dsr,代码行数:8,代码来源:ipt_DADDR.c
示例18: tcp_mss_csum_update
static inline void
tcp_mss_csum_update(struct tcphdr *tcph, __be16 oldmss, __be16 newmss)
{
/* do checksum later */
if (!sysctl_ip_vs_csum_offload)
tcph->check = csum_fold(ip_vs_check_diff2(oldmss, newmss,
~csum_unfold(tcph->check)));
}
开发者ID:Addision,项目名称:LVS,代码行数:8,代码来源:ip_vs_proto_tcp.c
示例19: icmp_error
/* Small and modified version of icmp_rcv */
static int
icmp_error(struct sk_buff *skb, enum ip_conntrack_info *ctinfo,
unsigned int hooknum)
{
struct icmphdr _ih, *icmph;
/* Not enough header? */
icmph = skb_header_pointer(skb, skb->nh.iph->ihl*4, sizeof(_ih), &_ih);
if (icmph == NULL) {
if (LOG_INVALID(IPPROTO_ICMP))
nf_log_packet(PF_INET, 0, skb, NULL, NULL, NULL,
"ip_ct_icmp: short packet ");
return -NF_ACCEPT;
}
/* See ip_conntrack_proto_tcp.c */
if (hooknum != NF_IP_PRE_ROUTING)
goto checksum_skipped;
switch (skb->ip_summed) {
case CHECKSUM_HW:
if (!(u16)csum_fold(skb->csum))
break;
/* fall through */
case CHECKSUM_NONE:
skb->csum = 0;
if (__skb_checksum_complete(skb)) {
if (LOG_INVALID(IPPROTO_ICMP))
nf_log_packet(PF_INET, 0, skb, NULL, NULL, NULL,
"ip_ct_icmp: bad ICMP checksum ");
return -NF_ACCEPT;
}
}
checksum_skipped:
/*
* 18 is the highest 'known' ICMP type. Anything else is a mystery
*
* RFC 1122: 3.2.2 Unknown ICMP messages types MUST be silently
* discarded.
*/
if (icmph->type > NR_ICMP_TYPES) {
if (LOG_INVALID(IPPROTO_ICMP))
nf_log_packet(PF_INET, 0, skb, NULL, NULL, NULL,
"ip_ct_icmp: invalid ICMP type ");
return -NF_ACCEPT;
}
/* Need to track icmp error message? */
if (icmph->type != ICMP_DEST_UNREACH
&& icmph->type != ICMP_SOURCE_QUENCH
&& icmph->type != ICMP_TIME_EXCEEDED
&& icmph->type != ICMP_PARAMETERPROB
&& icmph->type != ICMP_REDIRECT)
return NF_ACCEPT;
return icmp_error_message(skb, ctinfo, hooknum);
}
开发者ID:KrisChaplin,项目名称:LRT2x4_v1.0.2.06_GPL_source,代码行数:59,代码来源:ip_conntrack_proto_icmp.c
示例20: pim6_rcv
static int pim6_rcv(struct sk_buff *skb)
{
struct pimreghdr *pim;
struct ipv6hdr *encap;
struct net_device *reg_dev = NULL;
struct net *net = dev_net(skb->dev);
int reg_vif_num = net->ipv6.mroute_reg_vif_num;
if (!pskb_may_pull(skb, sizeof(*pim) + sizeof(*encap)))
goto drop;
pim = (struct pimreghdr *)skb_transport_header(skb);
if (pim->type != ((PIM_VERSION << 4) | PIM_REGISTER) ||
(pim->flags & PIM_NULL_REGISTER) ||
(csum_ipv6_magic(&ipv6_hdr(skb)->saddr, &ipv6_hdr(skb)->daddr,
sizeof(*pim), IPPROTO_PIM,
csum_partial((void *)pim, sizeof(*pim), 0)) &&
csum_fold(skb_checksum(skb, 0, skb->len, 0))))
goto drop;
/* check if the inner packet is destined to mcast group */
encap = (struct ipv6hdr *)(skb_transport_header(skb) +
sizeof(*pim));
if (!ipv6_addr_is_multicast(&encap->daddr) ||
encap->payload_len == 0 ||
ntohs(encap->payload_len) + sizeof(*pim) > skb->len)
goto drop;
read_lock(&mrt_lock);
if (reg_vif_num >= 0)
reg_dev = net->ipv6.vif6_table[reg_vif_num].dev;
if (reg_dev)
dev_hold(reg_dev);
read_unlock(&mrt_lock);
if (reg_dev == NULL)
goto drop;
skb->mac_header = skb->network_header;
skb_pull(skb, (u8 *)encap - skb->data);
skb_reset_network_header(skb);
skb->dev = reg_dev;
skb->protocol = htons(ETH_P_IPV6);
skb->ip_summed = 0;
skb->pkt_type = PACKET_HOST;
skb_dst_drop(skb);
reg_dev->stats.rx_bytes += skb->len;
reg_dev->stats.rx_packets++;
nf_reset(skb);
netif_rx(skb);
dev_put(reg_dev);
return 0;
drop:
kfree_skb(skb);
return 0;
}
开发者ID:vps2fast,项目名称:openvz-kernel,代码行数:57,代码来源:ip6mr.c
注:本文中的csum_fold函数示例由纯净天空整理自Github/MSDocs等源码及文档管理平台,相关代码片段筛选自各路编程大神贡献的开源项目,源码版权归原作者所有,传播和使用请参考对应项目的License;未经允许,请勿转载。 |
请发表评论