📄 xfrm_user.c
字号:
+ nla_total_size(sizeof(struct xfrmu_spdhinfo));}static int build_spdinfo(struct sk_buff *skb, u32 pid, u32 seq, u32 flags){ struct xfrmk_spdinfo si; struct xfrmu_spdinfo spc; struct xfrmu_spdhinfo sph; struct nlmsghdr *nlh; u32 *f; nlh = nlmsg_put(skb, pid, seq, XFRM_MSG_NEWSPDINFO, sizeof(u32), 0); if (nlh == NULL) /* shouldnt really happen ... */ return -EMSGSIZE; f = nlmsg_data(nlh); *f = flags; xfrm_spd_getinfo(&si); spc.incnt = si.incnt; spc.outcnt = si.outcnt; spc.fwdcnt = si.fwdcnt; spc.inscnt = si.inscnt; spc.outscnt = si.outscnt; spc.fwdscnt = si.fwdscnt; sph.spdhcnt = si.spdhcnt; sph.spdhmcnt = si.spdhmcnt; NLA_PUT(skb, XFRMA_SPD_INFO, sizeof(spc), &spc); NLA_PUT(skb, XFRMA_SPD_HINFO, sizeof(sph), &sph); return nlmsg_end(skb, nlh);nla_put_failure: nlmsg_cancel(skb, nlh); return -EMSGSIZE;}static int xfrm_get_spdinfo(struct sk_buff *skb, struct nlmsghdr *nlh, struct nlattr **attrs){ struct sk_buff *r_skb; u32 *flags = nlmsg_data(nlh); u32 spid = NETLINK_CB(skb).pid; u32 seq = nlh->nlmsg_seq; r_skb = nlmsg_new(xfrm_spdinfo_msgsize(), GFP_ATOMIC); if (r_skb == NULL) return -ENOMEM; if (build_spdinfo(r_skb, spid, seq, *flags) < 0) BUG(); return nlmsg_unicast(xfrm_nl, r_skb, spid);}static inline size_t xfrm_sadinfo_msgsize(void){ return NLMSG_ALIGN(4) + nla_total_size(sizeof(struct xfrmu_sadhinfo)) + nla_total_size(4); /* XFRMA_SAD_CNT */}static int build_sadinfo(struct sk_buff *skb, u32 pid, u32 seq, u32 flags){ struct xfrmk_sadinfo si; struct xfrmu_sadhinfo sh; struct nlmsghdr *nlh; u32 *f; nlh = nlmsg_put(skb, pid, seq, XFRM_MSG_NEWSADINFO, sizeof(u32), 0); if (nlh == NULL) /* shouldnt really happen ... */ return -EMSGSIZE; f = nlmsg_data(nlh); *f = flags; xfrm_sad_getinfo(&si); sh.sadhmcnt = si.sadhmcnt; sh.sadhcnt = si.sadhcnt; NLA_PUT_U32(skb, XFRMA_SAD_CNT, si.sadcnt); NLA_PUT(skb, XFRMA_SAD_HINFO, sizeof(sh), &sh); return nlmsg_end(skb, nlh);nla_put_failure: nlmsg_cancel(skb, nlh); return -EMSGSIZE;}static int xfrm_get_sadinfo(struct sk_buff *skb, struct nlmsghdr *nlh, struct nlattr **attrs){ struct sk_buff *r_skb; u32 *flags = nlmsg_data(nlh); u32 spid = NETLINK_CB(skb).pid; u32 seq = nlh->nlmsg_seq; r_skb = nlmsg_new(xfrm_sadinfo_msgsize(), GFP_ATOMIC); if (r_skb == NULL) return -ENOMEM; if (build_sadinfo(r_skb, spid, seq, *flags) < 0) BUG(); return nlmsg_unicast(xfrm_nl, r_skb, spid);}static int xfrm_get_sa(struct sk_buff *skb, struct nlmsghdr *nlh, struct nlattr **attrs){ struct xfrm_usersa_id *p = nlmsg_data(nlh); struct xfrm_state *x; struct sk_buff *resp_skb; int err = -ESRCH; x = xfrm_user_state_lookup(p, attrs, &err); if (x == NULL) goto out_noput; resp_skb = xfrm_state_netlink(skb, x, nlh->nlmsg_seq); if (IS_ERR(resp_skb)) { err = PTR_ERR(resp_skb); } else { err = nlmsg_unicast(xfrm_nl, resp_skb, NETLINK_CB(skb).pid); } xfrm_state_put(x);out_noput: return err;}static int verify_userspi_info(struct xfrm_userspi_info *p){ switch (p->info.id.proto) { case IPPROTO_AH: case IPPROTO_ESP: break; case IPPROTO_COMP: /* IPCOMP spi is 16-bits. */ if (p->max >= 0x10000) return -EINVAL; break; default: return -EINVAL; } if (p->min > p->max) return -EINVAL; return 0;}static int xfrm_alloc_userspi(struct sk_buff *skb, struct nlmsghdr *nlh, struct nlattr **attrs){ struct xfrm_state *x; struct xfrm_userspi_info *p; struct sk_buff *resp_skb; xfrm_address_t *daddr; int family; int err; p = nlmsg_data(nlh); err = verify_userspi_info(p); if (err) goto out_noput; family = p->info.family; daddr = &p->info.id.daddr; x = NULL; if (p->info.seq) { x = xfrm_find_acq_byseq(p->info.seq); if (x && xfrm_addr_cmp(&x->id.daddr, daddr, family)) { xfrm_state_put(x); x = NULL; } } if (!x) x = xfrm_find_acq(p->info.mode, p->info.reqid, p->info.id.proto, daddr, &p->info.saddr, 1, family); err = -ENOENT; if (x == NULL) goto out_noput; err = xfrm_alloc_spi(x, p->min, p->max); if (err) goto out; resp_skb = xfrm_state_netlink(skb, x, nlh->nlmsg_seq); if (IS_ERR(resp_skb)) { err = PTR_ERR(resp_skb); goto out; } err = nlmsg_unicast(xfrm_nl, resp_skb, NETLINK_CB(skb).pid);out: xfrm_state_put(x);out_noput: return err;}static int verify_policy_dir(u8 dir){ switch (dir) { case XFRM_POLICY_IN: case XFRM_POLICY_OUT: case XFRM_POLICY_FWD: break; default: return -EINVAL; } return 0;}static int verify_policy_type(u8 type){ switch (type) { case XFRM_POLICY_TYPE_MAIN:#ifdef CONFIG_XFRM_SUB_POLICY case XFRM_POLICY_TYPE_SUB:#endif break; default: return -EINVAL; } return 0;}static int verify_newpolicy_info(struct xfrm_userpolicy_info *p){ switch (p->share) { case XFRM_SHARE_ANY: case XFRM_SHARE_SESSION: case XFRM_SHARE_USER: case XFRM_SHARE_UNIQUE: break; default: return -EINVAL; } switch (p->action) { case XFRM_POLICY_ALLOW: case XFRM_POLICY_BLOCK: break; default: return -EINVAL; } switch (p->sel.family) { case AF_INET: break; case AF_INET6:#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) break;#else return -EAFNOSUPPORT;#endif default: return -EINVAL; } return verify_policy_dir(p->dir);}static int copy_from_user_sec_ctx(struct xfrm_policy *pol, struct nlattr **attrs){ struct nlattr *rt = attrs[XFRMA_SEC_CTX]; struct xfrm_user_sec_ctx *uctx; if (!rt) return 0; uctx = nla_data(rt); return security_xfrm_policy_alloc(pol, uctx);}static void copy_templates(struct xfrm_policy *xp, struct xfrm_user_tmpl *ut, int nr){ int i; xp->xfrm_nr = nr; for (i = 0; i < nr; i++, ut++) { struct xfrm_tmpl *t = &xp->xfrm_vec[i]; memcpy(&t->id, &ut->id, sizeof(struct xfrm_id)); memcpy(&t->saddr, &ut->saddr, sizeof(xfrm_address_t)); t->reqid = ut->reqid; t->mode = ut->mode; t->share = ut->share; t->optional = ut->optional; t->aalgos = ut->aalgos; t->ealgos = ut->ealgos; t->calgos = ut->calgos; t->encap_family = ut->family; }}static int validate_tmpl(int nr, struct xfrm_user_tmpl *ut, u16 family){ int i; if (nr > XFRM_MAX_DEPTH) return -EINVAL; for (i = 0; i < nr; i++) { /* We never validated the ut->family value, so many * applications simply leave it at zero. The check was * never made and ut->family was ignored because all * templates could be assumed to have the same family as * the policy itself. Now that we will have ipv4-in-ipv6 * and ipv6-in-ipv4 tunnels, this is no longer true. */ if (!ut[i].family) ut[i].family = family; switch (ut[i].family) { case AF_INET: break;#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) case AF_INET6: break;#endif default: return -EINVAL; } } return 0;}static int copy_from_user_tmpl(struct xfrm_policy *pol, struct nlattr **attrs){ struct nlattr *rt = attrs[XFRMA_TMPL]; if (!rt) { pol->xfrm_nr = 0; } else { struct xfrm_user_tmpl *utmpl = nla_data(rt); int nr = nla_len(rt) / sizeof(*utmpl); int err; err = validate_tmpl(nr, utmpl, pol->family); if (err) return err; copy_templates(pol, utmpl, nr); } return 0;}static int copy_from_user_policy_type(u8 *tp, struct nlattr **attrs){ struct nlattr *rt = attrs[XFRMA_POLICY_TYPE]; struct xfrm_userpolicy_type *upt; u8 type = XFRM_POLICY_TYPE_MAIN; int err; if (rt) { upt = nla_data(rt); type = upt->type; } err = verify_policy_type(type); if (err) return err; *tp = type; return 0;}static void copy_from_user_policy(struct xfrm_policy *xp, struct xfrm_userpolicy_info *p){ xp->priority = p->priority; xp->index = p->index; memcpy(&xp->selector, &p->sel, sizeof(xp->selector)); memcpy(&xp->lft, &p->lft, sizeof(xp->lft)); xp->action = p->action; xp->flags = p->flags; xp->family = p->sel.family; /* XXX xp->share = p->share; */}static void copy_to_user_policy(struct xfrm_policy *xp, struct xfrm_userpolicy_info *p, int dir){ memcpy(&p->sel, &xp->selector, sizeof(p->sel)); memcpy(&p->lft, &xp->lft, sizeof(p->lft)); memcpy(&p->curlft, &xp->curlft, sizeof(p->curlft)); p->priority = xp->priority; p->index = xp->index; p->sel.family = xp->family; p->dir = dir; p->action = xp->action; p->flags = xp->flags; p->share = XFRM_SHARE_ANY; /* XXX xp->share */}static struct xfrm_policy *xfrm_policy_construct(struct xfrm_userpolicy_info *p, struct nlattr **attrs, int *errp){ struct xfrm_policy *xp = xfrm_policy_alloc(GFP_KERNEL); int err; if (!xp) { *errp = -ENOMEM; return NULL; } copy_from_user_policy(xp, p); err = copy_from_user_policy_type(&xp->type, attrs); if (err) goto error; if (!(err = copy_from_user_tmpl(xp, attrs))) err = copy_from_user_sec_ctx(xp, attrs); if (err) goto error; return xp; error: *errp = err; kfree(xp); return NULL;}static int xfrm_add_policy(struct sk_buff *skb, struct nlmsghdr *nlh, struct nlattr **attrs){ struct xfrm_userpolicy_info *p = nlmsg_data(nlh); struct xfrm_policy *xp; struct km_event c; int err; int excl; err = verify_newpolicy_info(p); if (err) return err; err = verify_sec_ctx_len(attrs); if (err) return err; xp = xfrm_policy_construct(p, attrs, &err); if (!xp) return err; /* shouldnt excl be based on nlh flags?? * Aha! this is anti-netlink really i.e more pfkey derived * in netlink excl is a flag and you wouldnt need * a type XFRM_MSG_UPDPOLICY - JHS */ excl = nlh->nlmsg_type == XFRM_MSG_NEWPOLICY; err = xfrm_policy_insert(p->dir, xp, excl); xfrm_audit_policy_add(xp, err ? 0 : 1, NETLINK_CB(skb).loginuid, NETLINK_CB(skb).sid); if (err) { security_xfrm_policy_free(xp); kfree(xp); return err; } c.event = nlh->nlmsg_type; c.seq = nlh->nlmsg_seq; c.pid = nlh->nlmsg_pid; km_policy_notify(xp, p->dir, &c); xfrm_pol_put(xp); return 0;}static int copy_to_user_tmpl(struct xfrm_policy *xp, struct sk_buff *skb){ struct xfrm_user_tmpl vec[XFRM_MAX_DEPTH]; int i; if (xp->xfrm_nr == 0) return 0; for (i = 0; i < xp->xfrm_nr; i++) { struct xfrm_user_tmpl *up = &vec[i]; struct xfrm_tmpl *kp = &xp->xfrm_vec[i]; memcpy(&up->id, &kp->id, sizeof(up->id)); up->family = kp->encap_family; memcpy(&up->saddr, &kp->saddr, sizeof(up->saddr)); up->reqid = kp->reqid; up->mode = kp->mode; up->share = kp->share; up->optional = kp->optional; up->aalgos = kp->aalgos; up->ealgos = kp->ealgos; up->calgos = kp->calgos; } return nla_put(skb, XFRMA_TMPL, sizeof(struct xfrm_user_tmpl) * xp->xfrm_nr, vec);}static inline int copy_to_user_state_sec_ctx(struct xfrm_state *x, struct sk_buff *skb){ if (x->security) { return copy_sec_ctx(x->security, skb); } return 0;}static inline int copy_to_user_sec_ctx(struct xfrm_policy *xp, struct sk_buff *skb){ if (xp->security) { return copy_sec_ctx(xp->security, skb); } return 0;}static inline size_t userpolicy_type_attrsize(void){#ifdef CONFIG_XFRM_SUB_POLICY return nla_total_size(sizeof(struct xfrm_userpolicy_type));#else return 0;#endif}#ifdef CONFIG_XFRM_SUB_POLICYstatic int copy_to_user_policy_type(u8 type, struct sk_buff *skb){ struct xfrm_userpolicy_type upt = { .type = type, }; return nla_put(skb, XFRMA_POLICY_TYPE, sizeof(upt), &upt);}#elsestatic inline int copy_to_user_policy_type(u8 type, struct sk_buff *skb){ return 0;}#endifstatic int dump_one_policy(struct xfrm_policy *xp, int dir, int count, void *ptr){ struct xfrm_dump_info *sp = ptr; struct xfrm_userpolicy_info *p; struct sk_buff *in_skb = sp->in_skb; struct sk_buff *skb = sp->out_skb; struct nlmsghdr *nlh; if (sp->this_idx < sp->start_idx) goto out; nlh = nlmsg_put(skb, NETLINK_CB(in_skb).pid, sp->nlmsg_seq, XFRM_MSG_NEWPOLICY, sizeof(*p), sp->nlmsg_flags); if (nlh == NULL) return -EMSGSIZE; p = nlmsg_data(nlh); copy_to_user_policy(xp, p, dir); if (copy_to_user_tmpl(xp, skb) < 0) goto nlmsg_failure; if (copy_to_user_sec_ctx(xp, skb)) goto nlmsg_failure; if (copy_to_user_policy_type(xp->type, skb) < 0) goto nlmsg_failure; nlmsg_end(skb, nlh);out: sp->this_idx++; return 0;nlmsg_failure: nlmsg_cancel(skb, nlh); return -EMSGSIZE;}static int xfrm_dump_policy(struct sk_buff *skb, struct netlink_callback *cb){ struct xfrm_dump_info info; info.in_skb = cb->skb; info.out_skb = skb; info.nlmsg_seq = cb->nlh->nlmsg_seq; info.nlmsg_flags = NLM_F_MULTI; info.this_idx = 0; info.start_idx = cb->args[0]; (void) xfrm_policy_walk(XFRM_POLICY_TYPE_MAIN, dump_one_policy, &info);#ifdef CONFIG_XFRM_SUB_POLICY (void) xfrm_policy_walk(XFRM_POLICY_TYPE_SUB, dump_one_policy, &info);#endif cb->args[0] = info.this_idx; return skb->len;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -