Do the first BOOTPARAM RPC call to the broadcast address instead of
using the address of the RARP server because a BOOTPARAM server might not be running on the machine that sent the RARP reply.
This commit is contained in:
parent
6b78d3957c
commit
62f18b1dda
33
sys/nfs/krpc.h
Normal file
33
sys/nfs/krpc.h
Normal file
@ -0,0 +1,33 @@
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
|
||||
int krpc_call __P((struct sockaddr_in *sin, \
|
||||
u_long prog, u_long vers, u_long func, \
|
||||
struct mbuf **data, struct mbuf **from));
|
||||
|
||||
int krpc_portmap __P((struct sockaddr_in *sin, \
|
||||
u_long prog, u_long vers, u_short *portp));
|
||||
|
||||
|
||||
/*
|
||||
* RPC definitions for the portmapper
|
||||
*/
|
||||
#define PMAPPORT 111
|
||||
#define PMAPPROG 100000
|
||||
#define PMAPVERS 2
|
||||
#define PMAPPROC_NULL 0
|
||||
#define PMAPPROC_SET 1
|
||||
#define PMAPPROC_UNSET 2
|
||||
#define PMAPPROC_GETPORT 3
|
||||
#define PMAPPROC_DUMP 4
|
||||
#define PMAPPROC_CALLIT 5
|
||||
|
||||
|
||||
/*
|
||||
* RPC definitions for bootparamd
|
||||
*/
|
||||
#define BOOTPARAM_PROG 100026
|
||||
#define BOOTPARAM_VERS 1
|
||||
#define BOOTPARAM_WHOAMI 1
|
||||
#define BOOTPARAM_GETFILE 2
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: krpc_subr.c,v 1.6 1994/08/12 04:31:51 cgd Exp $ */
|
||||
/* $NetBSD: krpc_subr.c,v 1.7 1994/09/26 16:42:31 gwr Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1994 Gordon Ross, Adam Glass
|
||||
@ -56,6 +56,7 @@
|
||||
#include <netinet/in.h>
|
||||
|
||||
#include <nfs/rpcv2.h>
|
||||
#include <nfs/krpc.h>
|
||||
|
||||
/*
|
||||
* Kernel support for Sun RPC
|
||||
@ -65,11 +66,6 @@
|
||||
* Note: will not work on variable-sized rpc args/results.
|
||||
* implicit size-limit of an mbuf.
|
||||
*/
|
||||
|
||||
#define PMAPPORT 111
|
||||
#define PMAPPROG 100000
|
||||
#define PMAPVERS 2
|
||||
#define PMAPPROC_GETPORT 3
|
||||
|
||||
/*
|
||||
* Generic RPC headers
|
||||
@ -119,8 +115,8 @@ struct rpc_reply {
|
||||
* Returns non-zero error on failure.
|
||||
*/
|
||||
int
|
||||
krpc_portmap(sa, prog, vers, portp)
|
||||
struct sockaddr *sa; /* server address */
|
||||
krpc_portmap(sin, prog, vers, portp)
|
||||
struct sockaddr_in *sin; /* server address */
|
||||
u_long prog, vers; /* host order */
|
||||
u_short *portp; /* network order */
|
||||
{
|
||||
@ -156,8 +152,9 @@ krpc_portmap(sa, prog, vers, portp)
|
||||
sdata->proto = htonl(IPPROTO_UDP);
|
||||
sdata->port = 0;
|
||||
|
||||
error = krpc_call(sa, PMAPPROG, PMAPVERS,
|
||||
PMAPPROC_GETPORT, &m);
|
||||
sin->sin_port = htons(PMAPPORT);
|
||||
error = krpc_call(sin, PMAPPROG, PMAPVERS,
|
||||
PMAPPROC_GETPORT, &m, NULL);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
@ -170,17 +167,19 @@ krpc_portmap(sa, prog, vers, portp)
|
||||
|
||||
/*
|
||||
* Do a remote procedure call (RPC) and wait for its reply.
|
||||
* If from_p is non-null, then we are doing broadcast, and
|
||||
* the address from whence the response came is saved there.
|
||||
*/
|
||||
int
|
||||
krpc_call(sa, prog, vers, func, data)
|
||||
struct sockaddr *sa;
|
||||
krpc_call(sa, prog, vers, func, data, from_p)
|
||||
struct sockaddr_in *sa;
|
||||
u_long prog, vers, func;
|
||||
struct mbuf **data; /* input/output */
|
||||
struct mbuf **from_p; /* output */
|
||||
{
|
||||
struct socket *so;
|
||||
struct sockaddr_in *sin;
|
||||
struct timeval *tv;
|
||||
struct mbuf *m, *nam, *mhead;
|
||||
struct mbuf *m, *nam, *mhead, *from;
|
||||
struct rpc_call *call;
|
||||
struct rpc_reply *reply;
|
||||
struct uio auio;
|
||||
@ -192,11 +191,12 @@ krpc_call(sa, prog, vers, func, data)
|
||||
* Validate address family.
|
||||
* Sorry, this is INET specific...
|
||||
*/
|
||||
if (sa->sa_family != AF_INET)
|
||||
if (sa->sin_family != AF_INET)
|
||||
return (EAFNOSUPPORT);
|
||||
|
||||
/* Free at end if not null. */
|
||||
nam = mhead = NULL;
|
||||
from = NULL;
|
||||
|
||||
/*
|
||||
* Create socket and set its recieve timeout.
|
||||
@ -208,13 +208,32 @@ krpc_call(sa, prog, vers, func, data)
|
||||
if (m == NULL) {
|
||||
error = ENOBUFS;
|
||||
goto out;
|
||||
} else {
|
||||
struct timeval *tv;
|
||||
tv = mtod(m, struct timeval *);
|
||||
m->m_len = sizeof(*tv);
|
||||
tv->tv_sec = 1;
|
||||
tv->tv_usec = 0;
|
||||
if ((error = sosetopt(so, SOL_SOCKET, SO_RCVTIMEO, m)))
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* Enable broadcast if necessary.
|
||||
*/
|
||||
if (from_p) {
|
||||
int *on;
|
||||
m = m_get(M_WAIT, MT_SOOPTS);
|
||||
if (m == NULL) {
|
||||
error = ENOBUFS;
|
||||
goto out;
|
||||
}
|
||||
on = mtod(m, int *);
|
||||
m->m_len = sizeof(*on);
|
||||
*on = 1;
|
||||
if ((error = sosetopt(so, SOL_SOCKET, SO_BROADCAST, m)))
|
||||
goto out;
|
||||
}
|
||||
tv = mtod(m, struct timeval *);
|
||||
m->m_len = sizeof(*tv);
|
||||
tv->tv_sec = 1;
|
||||
tv->tv_usec = 0;
|
||||
if ((error = sosetopt(so, SOL_SOCKET, SO_RCVTIMEO, m)))
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* Bind the local endpoint to a reserved port,
|
||||
@ -248,13 +267,7 @@ krpc_call(sa, prog, vers, func, data)
|
||||
goto out;
|
||||
}
|
||||
sin = mtod(nam, struct sockaddr_in *);
|
||||
bcopy((caddr_t)sa, (caddr_t)sin, (nam->m_len = sa->sa_len));
|
||||
|
||||
/*
|
||||
* Set the port number that the request will use.
|
||||
*/
|
||||
if ((error = krpc_portmap(sa, prog, vers, &sin->sin_port)))
|
||||
goto out;
|
||||
bcopy((caddr_t)sa, (caddr_t)sin, (nam->m_len = sa->sin_len));
|
||||
|
||||
/*
|
||||
* Prepend RPC message header.
|
||||
@ -280,7 +293,8 @@ krpc_call(sa, prog, vers, func, data)
|
||||
*/
|
||||
call = mtod(mhead, struct rpc_call *);
|
||||
bzero((caddr_t)call, sizeof(*call));
|
||||
call->rp_xid = ++xid; /* no need to put in network order */
|
||||
xid++;
|
||||
call->rp_xid = htonl(xid);
|
||||
/* call->rp_direction = 0; */
|
||||
call->rp_rpcvers = htonl(2);
|
||||
call->rp_prog = htonl(prog);
|
||||
@ -322,9 +336,17 @@ krpc_call(sa, prog, vers, func, data)
|
||||
*/
|
||||
secs = timo;
|
||||
while (secs > 0) {
|
||||
if (from) {
|
||||
m_freem(from);
|
||||
from = NULL;
|
||||
}
|
||||
if (m) {
|
||||
m_freem(m);
|
||||
m = NULL;
|
||||
}
|
||||
auio.uio_resid = len = 1<<16;
|
||||
rcvflg = 0;
|
||||
error = soreceive(so, NULL, &auio, &m, NULL, &rcvflg);
|
||||
error = soreceive(so, &from, &auio, &m, NULL, &rcvflg);
|
||||
if (error == EWOULDBLOCK) {
|
||||
secs--;
|
||||
continue;
|
||||
@ -333,20 +355,35 @@ krpc_call(sa, prog, vers, func, data)
|
||||
goto out;
|
||||
len -= auio.uio_resid;
|
||||
|
||||
/* Is the reply complete and the right one? */
|
||||
if (len < MIN_REPLY_HDR) {
|
||||
m_freem(m);
|
||||
/* Does the reply contain at least a header? */
|
||||
if (len < MIN_REPLY_HDR)
|
||||
continue;
|
||||
if (m->m_len < MIN_REPLY_HDR)
|
||||
continue;
|
||||
reply = mtod(m, struct rpc_reply *);
|
||||
|
||||
/* Is it the right reply? */
|
||||
if (reply->rp_direction != htonl(RPC_REPLY))
|
||||
continue;
|
||||
|
||||
if (reply->rp_xid != htonl(xid))
|
||||
continue;
|
||||
|
||||
/* Was RPC accepted? (authorization OK) */
|
||||
if (reply->rp_astatus != 0) {
|
||||
error = ntohl(reply->rp_u.rpu_errno);
|
||||
printf("rpc denied, error=%d\n", error);
|
||||
continue;
|
||||
}
|
||||
if (m->m_len < MIN_REPLY_HDR) {
|
||||
m = m_pullup(m, MIN_REPLY_HDR);
|
||||
if (!m)
|
||||
continue;
|
||||
|
||||
/* Did the call succeed? */
|
||||
if ((error = ntohl(reply->rp_u.rpu_ok.rp_rstatus)) != 0) {
|
||||
printf("rpc status=%d\n", error);
|
||||
continue;
|
||||
}
|
||||
reply = mtod(m, struct rpc_reply *);
|
||||
if ((reply->rp_direction == htonl(RPC_REPLY)) &&
|
||||
(reply->rp_xid == xid))
|
||||
goto gotreply; /* break two levels */
|
||||
|
||||
goto gotreply; /* break two levels */
|
||||
|
||||
} /* while secs */
|
||||
} /* forever send/receive */
|
||||
|
||||
@ -373,22 +410,7 @@ krpc_call(sa, prog, vers, func, data)
|
||||
error = ENOBUFS;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
reply = mtod(m, struct rpc_reply *);
|
||||
|
||||
/*
|
||||
* Check RPC acceptance and status.
|
||||
*/
|
||||
if (reply->rp_astatus != 0) {
|
||||
error = ntohl(reply->rp_u.rpu_errno);
|
||||
printf("rpc denied, error=%d\n", error);
|
||||
m_freem(m);
|
||||
goto out;
|
||||
}
|
||||
if ((error = ntohl(reply->rp_u.rpu_ok.rp_rstatus)) != 0) {
|
||||
printf("rpc status=%d\n", error);
|
||||
m_freem(m);
|
||||
goto out;
|
||||
reply = mtod(m, struct rpc_reply *);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -403,10 +425,15 @@ krpc_call(sa, prog, vers, func, data)
|
||||
|
||||
/* result */
|
||||
*data = m;
|
||||
if (from_p) {
|
||||
*from_p = from;
|
||||
from = NULL;
|
||||
}
|
||||
|
||||
out:
|
||||
if (nam) m_freem(nam);
|
||||
if (mhead) m_freem(mhead);
|
||||
if (from) m_freem(from);
|
||||
soclose(so);
|
||||
return error;
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: nfs_boot.c,v 1.10 1994/08/11 23:47:51 mycroft Exp $ */
|
||||
/* $NetBSD: nfs_boot.c,v 1.11 1994/09/26 16:42:33 gwr Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1994 Adam Glass, Gordon Ross
|
||||
@ -48,9 +48,19 @@
|
||||
#include <nfs/nfsv2.h>
|
||||
#include <nfs/nfs.h>
|
||||
#include <nfs/nfsdiskless.h>
|
||||
#include <nfs/krpc.h>
|
||||
|
||||
#include "ether.h"
|
||||
#if NETHER > 0
|
||||
#if NETHER == 0
|
||||
|
||||
int nfs_boot_init(nd, procp)
|
||||
struct nfs_diskless *nd;
|
||||
struct proc *procp;
|
||||
{
|
||||
panic("nfs_boot_init: no ether");
|
||||
}
|
||||
|
||||
#else /* NETHER */
|
||||
|
||||
/*
|
||||
* Support for NFS diskless booting, specifically getting information
|
||||
@ -96,7 +106,7 @@ nfs_boot_init(nd, procp)
|
||||
struct proc *procp;
|
||||
{
|
||||
struct ifreq ireq;
|
||||
struct in_addr my_ip, srv_ip, gw_ip;
|
||||
struct in_addr my_ip, gw_ip;
|
||||
struct sockaddr_in bp_sin;
|
||||
struct sockaddr_in *sin;
|
||||
struct ifnet *ifp;
|
||||
@ -149,10 +159,9 @@ nfs_boot_init(nd, procp)
|
||||
* Do RARP for the interface address. Also
|
||||
* save the server address for bootparam RPC.
|
||||
*/
|
||||
if ((error = revarpwhoarewe(ifp, &srv_ip, &my_ip)) != 0)
|
||||
if ((error = revarpwhoami(&my_ip, ifp)) != 0)
|
||||
panic("revarp failed, error=%d", error);
|
||||
printf("nfs_boot: client=0x%x, server=0x%x\n",
|
||||
ntohl(my_ip.s_addr), ntohl(srv_ip.s_addr));
|
||||
printf("nfs_boot: client_addr=0x%x\n", ntohl(my_ip.s_addr));
|
||||
|
||||
/*
|
||||
* Do enough of ifconfig(8) so that the chosen interface can
|
||||
@ -173,17 +182,21 @@ nfs_boot_init(nd, procp)
|
||||
/*
|
||||
* Get client name and gateway address.
|
||||
* RPC: bootparam/whoami
|
||||
* XXX - Using old broadcast addr. for WHOAMI,
|
||||
* then it is replaced with the BP server addr.
|
||||
*/
|
||||
bzero((caddr_t)&bp_sin, sizeof(bp_sin));
|
||||
bp_sin.sin_len = sizeof(bp_sin);
|
||||
bp_sin.sin_family = AF_INET;
|
||||
bp_sin.sin_addr.s_addr = srv_ip.s_addr;
|
||||
bp_sin.sin_addr.s_addr = ~0; /* XXX */
|
||||
hostnamelen = MAXHOSTNAMELEN;
|
||||
|
||||
/* this returns gateway IP address */
|
||||
error = bp_whoami(&bp_sin, &my_ip, &gw_ip);
|
||||
if (error)
|
||||
panic("nfs_boot: bootparam whoami, error=%d", error);
|
||||
printf("nfs_boot: server_addr=0x%x\n",
|
||||
ntohl(bp_sin.sin_addr.s_addr));
|
||||
printf("nfs_boot: hostname=%s\n", hostname);
|
||||
|
||||
#ifdef NFS_BOOT_GATEWAY
|
||||
@ -315,16 +328,6 @@ struct bp_inaddr {
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* RPC definitions for bootparamd
|
||||
* (XXX - move to a header file?)
|
||||
*/
|
||||
#define BOOTPARAM_PROG 100026
|
||||
#define BOOTPARAM_VERS 1
|
||||
#define BOOTPARAM_WHOAMI 1
|
||||
#define BOOTPARAM_GETFILE 2
|
||||
|
||||
|
||||
/*
|
||||
* RPC: bootparam/whoami
|
||||
* Given client IP address, get:
|
||||
@ -334,6 +337,12 @@ struct bp_inaddr {
|
||||
*
|
||||
* Setting the hostname and domainname here may be somewhat
|
||||
* controvercial, but it is so easy to do it here. -gwr
|
||||
*
|
||||
* Note - bpsin is initialized to the broadcast address,
|
||||
* and will be replaced with the bootparam server address
|
||||
* after this call is complete. Have to use PMAP_PROC_CALL
|
||||
* to make sure we get responses only from a servers that
|
||||
* know about us (don't want to broadcast a getport call).
|
||||
*/
|
||||
static int
|
||||
bp_whoami(bpsin, my_ip, gw_ip)
|
||||
@ -341,42 +350,55 @@ bp_whoami(bpsin, my_ip, gw_ip)
|
||||
struct in_addr *my_ip;
|
||||
struct in_addr *gw_ip;
|
||||
{
|
||||
/* The RPC structures */
|
||||
struct bp_inaddr *bia;
|
||||
/* RPC structures for PMAPPROC_CALLIT */
|
||||
struct whoami_call {
|
||||
u_long call_prog;
|
||||
u_long call_vers;
|
||||
u_long call_proc;
|
||||
u_long call_arglen;
|
||||
struct bp_inaddr call_ia;
|
||||
} *call;
|
||||
|
||||
struct rpc_string *str;
|
||||
struct mbuf *m;
|
||||
struct bp_inaddr *bia;
|
||||
struct mbuf *m, *from;
|
||||
struct sockaddr_in *sin;
|
||||
int error, msg_len;
|
||||
int cn_len, dn_len;
|
||||
u_char *p;
|
||||
long *lp;
|
||||
|
||||
/*
|
||||
* Get message buffer of sufficient size.
|
||||
*/
|
||||
msg_len = sizeof(*bia);
|
||||
msg_len = sizeof(*call);
|
||||
m = m_get_len(msg_len);
|
||||
if (m == NULL)
|
||||
return ENOBUFS;
|
||||
|
||||
/*
|
||||
* Build request message.
|
||||
* Build request message for PMAPPROC_CALLIT.
|
||||
*/
|
||||
/* client IP address */
|
||||
bia = mtod(m, struct bp_inaddr *);
|
||||
bia->atype = htonl(1);
|
||||
p = (u_char*)my_ip; /* ugh! */
|
||||
bia->addr[0] = htonl(*p);
|
||||
p++;
|
||||
bia->addr[1] = htonl(*p);
|
||||
p++;
|
||||
bia->addr[2] = htonl(*p);
|
||||
p++;
|
||||
bia->addr[3] = htonl(*p);
|
||||
p++;
|
||||
call = mtod(m, struct whoami_call *);
|
||||
call->call_prog = BOOTPARAM_PROG;
|
||||
call->call_vers = BOOTPARAM_VERS;
|
||||
call->call_proc = BOOTPARAM_WHOAMI;
|
||||
call->call_arglen = sizeof(struct bp_inaddr);
|
||||
|
||||
/* RPC: bootparam/whoami */
|
||||
error = krpc_call((struct sockaddr *)bpsin,
|
||||
BOOTPARAM_PROG, BOOTPARAM_VERS, BOOTPARAM_WHOAMI, &m);
|
||||
/* client IP address */
|
||||
call->call_ia.atype = htonl(1);
|
||||
p = (u_char*)my_ip;
|
||||
lp = call->call_ia.addr;
|
||||
*lp++ = htonl(*p); p++;
|
||||
*lp++ = htonl(*p); p++;
|
||||
*lp++ = htonl(*p); p++;
|
||||
*lp++ = htonl(*p); p++;
|
||||
|
||||
/* RPC: portmap/callit */
|
||||
bpsin->sin_port = htons(PMAPPORT);
|
||||
from = NULL;
|
||||
error = krpc_call(bpsin, PMAPPROG, PMAPVERS,
|
||||
PMAPPROC_CALLIT, &m, &from);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
@ -384,7 +406,21 @@ bp_whoami(bpsin, my_ip, gw_ip)
|
||||
* Parse result message.
|
||||
*/
|
||||
msg_len = m->m_len;
|
||||
p = mtod(m, char *);
|
||||
lp = mtod(m, long *);
|
||||
|
||||
/* bootparam server port (also grab from address). */
|
||||
if (msg_len < sizeof(*lp))
|
||||
goto bad;
|
||||
msg_len -= sizeof(*lp);
|
||||
bpsin->sin_port = (short) *lp++;
|
||||
sin = mtod(from, struct sockaddr_in *);
|
||||
bpsin->sin_addr.s_addr = sin->sin_addr.s_addr;
|
||||
|
||||
/* length of encapsulated results */
|
||||
if (msg_len < (*lp + sizeof(*lp)))
|
||||
goto bad;
|
||||
msg_len = *lp++;
|
||||
p = (char*)lp;
|
||||
|
||||
/* client name */
|
||||
if (msg_len < sizeof(*str))
|
||||
@ -434,6 +470,8 @@ bad:
|
||||
error = EBADRPC;
|
||||
|
||||
out:
|
||||
if (from)
|
||||
m_freem(from);
|
||||
m_freem(m);
|
||||
return(error);
|
||||
}
|
||||
@ -490,8 +528,8 @@ bp_getfile(bpsin, key, md_sin, serv_name, pathname)
|
||||
bcopy(key, str->data, key_len);
|
||||
|
||||
/* RPC: bootparam/getfile */
|
||||
error = krpc_call((struct sockaddr *)bpsin,
|
||||
BOOTPARAM_PROG, BOOTPARAM_VERS, BOOTPARAM_GETFILE, &m);
|
||||
error = krpc_call(bpsin, BOOTPARAM_PROG, BOOTPARAM_VERS,
|
||||
BOOTPARAM_GETFILE, &m, NULL);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
@ -576,6 +614,11 @@ md_mount(mdsin, path, fhp)
|
||||
struct mbuf *m;
|
||||
int error, mlen, slen;
|
||||
|
||||
/* Get port number for MOUNTD. */
|
||||
error = krpc_portmap(mdsin, RPCPROG_MNT, RPCMNT_VER1,
|
||||
&mdsin->sin_port);
|
||||
if (error) return error;
|
||||
|
||||
slen = strlen(path);
|
||||
mlen = RPC_STR_SIZE(slen);
|
||||
|
||||
@ -587,8 +630,8 @@ md_mount(mdsin, path, fhp)
|
||||
bcopy(path, str->data, slen);
|
||||
|
||||
/* Do RPC to mountd. */
|
||||
error = krpc_call((struct sockaddr *)mdsin,
|
||||
RPCPROG_MNT, RPCMNT_VER1, RPCMNT_MOUNT, &m);
|
||||
error = krpc_call(mdsin, RPCPROG_MNT, RPCMNT_VER1,
|
||||
RPCMNT_MOUNT, &m, NULL);
|
||||
if (error)
|
||||
return error; /* message already freed */
|
||||
|
||||
@ -602,8 +645,8 @@ md_mount(mdsin, path, fhp)
|
||||
bcopy(rdata->fh, fhp, NFS_FHSIZE);
|
||||
|
||||
/* Set port number for NFS use. */
|
||||
error = krpc_portmap((struct sockaddr *)mdsin,
|
||||
NFS_PROG, NFS_VER2, &mdsin->sin_port);
|
||||
error = krpc_portmap(mdsin, NFS_PROG, NFS_VER2,
|
||||
&mdsin->sin_port);
|
||||
goto out;
|
||||
|
||||
bad:
|
||||
@ -614,13 +657,4 @@ out:
|
||||
return error;
|
||||
}
|
||||
|
||||
#else /* NETHER */
|
||||
|
||||
int nfs_boot_init(nd, procp)
|
||||
struct nfs_diskless *nd;
|
||||
struct proc *procp;
|
||||
{
|
||||
panic("nfs_boot_init: no ether");
|
||||
}
|
||||
|
||||
#endif /* NETHER */
|
||||
|
Loading…
Reference in New Issue
Block a user