Have sockd apply BPF filters to ARP sockets.
This commit is contained in:
		
							
								
								
									
										161
									
								
								ndhc/arp.c
									
									
									
									
									
								
							
							
						
						
									
										161
									
								
								ndhc/arp.c
									
									
									
									
									
								
							@@ -25,6 +25,7 @@
 | 
			
		||||
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 | 
			
		||||
 * POSSIBILITY OF SUCH DAMAGE.
 | 
			
		||||
 */
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
@@ -123,95 +124,51 @@ void arp_reset_send_stats(void)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void arp_set_bpf_basic(int fd)
 | 
			
		||||
static int get_arp_basic_socket(void)
 | 
			
		||||
{
 | 
			
		||||
    static struct sock_filter sf_arp[] = {
 | 
			
		||||
        // Verify that the frame has ethernet protocol type of ARP
 | 
			
		||||
        // and that the ARP hardware type field indicates Ethernet.
 | 
			
		||||
        BPF_STMT(BPF_LD + BPF_W + BPF_ABS, 12),
 | 
			
		||||
        BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, (ETH_P_ARP << 16) | ARPHRD_ETHER,
 | 
			
		||||
                 1, 0),
 | 
			
		||||
        BPF_STMT(BPF_RET + BPF_K, 0),
 | 
			
		||||
        // Verify that the ARP protocol type field indicates IP, the ARP
 | 
			
		||||
        // hardware address length field is 6, and the ARP protocol address
 | 
			
		||||
        // length field is 4.
 | 
			
		||||
        BPF_STMT(BPF_LD + BPF_W + BPF_ABS, 16),
 | 
			
		||||
        BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, (ETH_P_IP << 16) | 0x0604, 1, 0),
 | 
			
		||||
        BPF_STMT(BPF_RET + BPF_K, 0),
 | 
			
		||||
        // Sanity tests passed, so send all possible data.
 | 
			
		||||
        BPF_STMT(BPF_RET + BPF_K, 0x7fffffff),
 | 
			
		||||
    };
 | 
			
		||||
    static const struct sock_fprog sfp_arp = {
 | 
			
		||||
        .len = sizeof sf_arp / sizeof sf_arp[0],
 | 
			
		||||
        .filter = sf_arp,
 | 
			
		||||
    };
 | 
			
		||||
    using_arp_bpf = setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &sfp_arp,
 | 
			
		||||
                               sizeof sfp_arp) != -1;
 | 
			
		||||
    if (!using_arp_bpf)
 | 
			
		||||
        log_warning("%s: Failed to set BPF for ARP socket: %s",
 | 
			
		||||
                    client_config.interface, strerror(errno));
 | 
			
		||||
    char resp;
 | 
			
		||||
    int fd = request_sockd_fd("a", 1, &resp);
 | 
			
		||||
    switch (resp) {
 | 
			
		||||
        case 'A': using_arp_bpf = 1; break;
 | 
			
		||||
        case 'a': using_arp_bpf = 0; break;
 | 
			
		||||
        default: suicide("%s: (%s) expected a or A sockd reply but got %c",
 | 
			
		||||
                         client_config.interface, __func__, resp);
 | 
			
		||||
    }
 | 
			
		||||
    return fd;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void arp_set_bpf_defense(struct client_state_t *cs, int fd)
 | 
			
		||||
static int get_arp_defense_socket(struct client_state_t *cs)
 | 
			
		||||
{
 | 
			
		||||
    uint32_t mac4b;
 | 
			
		||||
    uint16_t mac2b;
 | 
			
		||||
    memcpy(&mac4b, client_config.arp, 4);
 | 
			
		||||
    memcpy(&mac2b, client_config.arp+4, 2);
 | 
			
		||||
 | 
			
		||||
    struct sock_filter sf_arp[] = {
 | 
			
		||||
        // Verify that the frame has ethernet protocol type of ARP
 | 
			
		||||
        // and that the ARP hardware type field indicates Ethernet.
 | 
			
		||||
        BPF_STMT(BPF_LD + BPF_W + BPF_ABS, 12),
 | 
			
		||||
        BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, (ETH_P_ARP << 16) | ARPHRD_ETHER,
 | 
			
		||||
                 1, 0),
 | 
			
		||||
        BPF_STMT(BPF_RET + BPF_K, 0),
 | 
			
		||||
        // Verify that the ARP protocol type field indicates IP, the ARP
 | 
			
		||||
        // hardware address length field is 6, and the ARP protocol address
 | 
			
		||||
        // length field is 4.
 | 
			
		||||
        BPF_STMT(BPF_LD + BPF_W + BPF_ABS, 16),
 | 
			
		||||
        BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, (ETH_P_IP << 16) | 0x0604, 1, 0),
 | 
			
		||||
        BPF_STMT(BPF_RET + BPF_K, 0),
 | 
			
		||||
 | 
			
		||||
        // If the ARP packet source IP does not match our IP address, then
 | 
			
		||||
        // it can be ignored.
 | 
			
		||||
        BPF_STMT(BPF_LD + BPF_W + BPF_ABS, 28),
 | 
			
		||||
        BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, cs->clientAddr, 1, 0),
 | 
			
		||||
        BPF_STMT(BPF_RET + BPF_K, 0),
 | 
			
		||||
        // If the first four bytes of the ARP packet source hardware address
 | 
			
		||||
        // does not equal our hardware address, then it's a conflict and should
 | 
			
		||||
        // be passed along.
 | 
			
		||||
        BPF_STMT(BPF_LD + BPF_W + BPF_ABS, 22),
 | 
			
		||||
        BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, mac4b, 1, 0),
 | 
			
		||||
        BPF_STMT(BPF_RET + BPF_K, 0x7fffffff),
 | 
			
		||||
        // If the last two bytes of the ARP packet source hardware address
 | 
			
		||||
        // do not equal our hardware address, then it's a conflict and should
 | 
			
		||||
        // be passed along.
 | 
			
		||||
        BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 26),
 | 
			
		||||
        BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, mac2b, 1, 0),
 | 
			
		||||
        BPF_STMT(BPF_RET + BPF_K, 0x7fffffff),
 | 
			
		||||
        // Packet announces our IP address and hardware address, so it requires
 | 
			
		||||
        // no action.
 | 
			
		||||
        BPF_STMT(BPF_RET + BPF_K, 0),
 | 
			
		||||
    };
 | 
			
		||||
    struct sock_fprog sfp_arp = {
 | 
			
		||||
        .len = sizeof sf_arp / sizeof sf_arp[0],
 | 
			
		||||
        .filter = (struct sock_filter *)sf_arp,
 | 
			
		||||
    };
 | 
			
		||||
    using_arp_bpf = setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &sfp_arp,
 | 
			
		||||
                               sizeof sfp_arp) != -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int get_arp_socket(void)
 | 
			
		||||
{
 | 
			
		||||
    return request_sockd_fd("a", 1, NULL);
 | 
			
		||||
    char buf[32];
 | 
			
		||||
    size_t buflen = 0;
 | 
			
		||||
    buf[0] = 'd';
 | 
			
		||||
    buflen += 1;
 | 
			
		||||
    memcpy(buf + buflen, &cs->clientAddr, sizeof cs->clientAddr);
 | 
			
		||||
    buflen += sizeof cs->clientAddr;
 | 
			
		||||
    memcpy(buf + buflen, client_config.arp, 6);
 | 
			
		||||
    buflen += 6;
 | 
			
		||||
    char resp;
 | 
			
		||||
    int fd = request_sockd_fd(buf, buflen, &resp);
 | 
			
		||||
    switch (resp) {
 | 
			
		||||
        case 'D': using_arp_bpf = 1; break;
 | 
			
		||||
        case 'd': using_arp_bpf = 0; break;
 | 
			
		||||
        default: suicide("%s: (%s) expected d or D sockd reply but got %c",
 | 
			
		||||
                         client_config.interface, __func__, resp);
 | 
			
		||||
    }
 | 
			
		||||
    return fd;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int arp_open_fd(struct client_state_t *cs)
 | 
			
		||||
{
 | 
			
		||||
    if (cs->arpFd != -1)
 | 
			
		||||
        return 0;
 | 
			
		||||
    cs->arpFd = get_arp_socket();
 | 
			
		||||
    switch (arpState) {
 | 
			
		||||
    default: cs->arpFd = -1; arpState = AS_NONE; return -1;
 | 
			
		||||
    case AS_COLLISION_CHECK:
 | 
			
		||||
    case AS_GW_QUERY:
 | 
			
		||||
    case AS_GW_CHECK: cs->arpFd = get_arp_basic_socket(); break;
 | 
			
		||||
    case AS_DEFENSE: cs->arpFd = get_arp_defense_socket(cs); break;
 | 
			
		||||
    }
 | 
			
		||||
    if (cs->arpFd == -1) {
 | 
			
		||||
        log_error("arp: Failed to create socket: %s", strerror(errno));
 | 
			
		||||
        return -1;
 | 
			
		||||
@@ -221,6 +178,17 @@ static int arp_open_fd(struct client_state_t *cs)
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int arp_min_close_fd(struct client_state_t *cs)
 | 
			
		||||
{
 | 
			
		||||
    if (cs->arpFd == -1)
 | 
			
		||||
        return 0;
 | 
			
		||||
    epoll_del(cs->epollFd, cs->arpFd);
 | 
			
		||||
    close(cs->arpFd);
 | 
			
		||||
    cs->arpFd = -1;
 | 
			
		||||
    arpState = AS_NONE;
 | 
			
		||||
    return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void arp_switch_state(struct client_state_t *cs, arp_state_t state)
 | 
			
		||||
{
 | 
			
		||||
    arp_state_t prev_state = arpState;
 | 
			
		||||
@@ -231,32 +199,14 @@ static void arp_switch_state(struct client_state_t *cs, arp_state_t state)
 | 
			
		||||
        arp_close_fd(cs);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    if (cs->arpFd == -1) {
 | 
			
		||||
    bool force_reopen = arpState == AS_DEFENSE || prev_state == AS_DEFENSE;
 | 
			
		||||
    if (force_reopen)
 | 
			
		||||
        arp_min_close_fd(cs);
 | 
			
		||||
    if (cs->arpFd == -1 || force_reopen) {
 | 
			
		||||
        if (arp_open_fd(cs) == -1)
 | 
			
		||||
            suicide("arp: Failed to open arpFd when changing state to %u",
 | 
			
		||||
                    arpState);
 | 
			
		||||
        if (arpState != AS_DEFENSE)
 | 
			
		||||
            arp_set_bpf_basic(cs->arpFd);
 | 
			
		||||
            suicide("arp: Failed to open arpFd when changing state %u -> %u",
 | 
			
		||||
                    prev_state, arpState);
 | 
			
		||||
    }
 | 
			
		||||
    if (arpState == AS_DEFENSE) {
 | 
			
		||||
        arp_set_bpf_defense(cs, cs->arpFd);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    if (prev_state == AS_DEFENSE) {
 | 
			
		||||
        arp_set_bpf_basic(cs->arpFd);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int arp_min_close_fd(struct client_state_t *cs)
 | 
			
		||||
{
 | 
			
		||||
    if (cs->arpFd == -1)
 | 
			
		||||
        return 0;
 | 
			
		||||
    epoll_del(cs->epollFd, cs->arpFd);
 | 
			
		||||
    close(cs->arpFd);
 | 
			
		||||
    cs->arpFd = -1;
 | 
			
		||||
    arpState = AS_NONE;
 | 
			
		||||
    return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int arp_close_fd(struct client_state_t *cs)
 | 
			
		||||
@@ -271,11 +221,6 @@ static int arp_reopen_fd(struct client_state_t *cs)
 | 
			
		||||
{
 | 
			
		||||
    arp_state_t prev_state = arpState;
 | 
			
		||||
    arp_min_close_fd(cs);
 | 
			
		||||
    if (arp_open_fd(cs) == -1) {
 | 
			
		||||
        log_warning("arp: Failed to re-open fd.  Something is very wrong.");
 | 
			
		||||
        log_warning("arp: Client will still run, but functionality will be degraded.");
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
    arp_switch_state(cs, prev_state);
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										123
									
								
								ndhc/sockd.c
									
									
									
									
									
								
							
							
						
						
									
										123
									
								
								ndhc/sockd.c
									
									
									
									
									
								
							@@ -34,6 +34,7 @@
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <signal.h>
 | 
			
		||||
#include <fcntl.h>
 | 
			
		||||
#include <assert.h>
 | 
			
		||||
#include <sys/socket.h>
 | 
			
		||||
#include <sys/signalfd.h>
 | 
			
		||||
#include <sys/epoll.h>
 | 
			
		||||
@@ -44,6 +45,7 @@
 | 
			
		||||
#include <netinet/ip.h>
 | 
			
		||||
#include <netpacket/packet.h>
 | 
			
		||||
#include <net/ethernet.h>
 | 
			
		||||
#include <netinet/if_ether.h>
 | 
			
		||||
#include <linux/filter.h>
 | 
			
		||||
#include <pwd.h>
 | 
			
		||||
#include <grp.h>
 | 
			
		||||
@@ -333,6 +335,108 @@ static int create_raw_broadcast_socket(void)
 | 
			
		||||
    return create_raw_socket(&da, NULL, NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool arp_set_bpf_basic(int fd)
 | 
			
		||||
{
 | 
			
		||||
    static struct sock_filter sf_arp[] = {
 | 
			
		||||
        // Verify that the frame has ethernet protocol type of ARP
 | 
			
		||||
        // and that the ARP hardware type field indicates Ethernet.
 | 
			
		||||
        BPF_STMT(BPF_LD + BPF_W + BPF_ABS, 12),
 | 
			
		||||
        BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, (ETH_P_ARP << 16) | ARPHRD_ETHER,
 | 
			
		||||
                 1, 0),
 | 
			
		||||
        BPF_STMT(BPF_RET + BPF_K, 0),
 | 
			
		||||
        // Verify that the ARP protocol type field indicates IP, the ARP
 | 
			
		||||
        // hardware address length field is 6, and the ARP protocol address
 | 
			
		||||
        // length field is 4.
 | 
			
		||||
        BPF_STMT(BPF_LD + BPF_W + BPF_ABS, 16),
 | 
			
		||||
        BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, (ETH_P_IP << 16) | 0x0604, 1, 0),
 | 
			
		||||
        BPF_STMT(BPF_RET + BPF_K, 0),
 | 
			
		||||
        // Sanity tests passed, so send all possible data.
 | 
			
		||||
        BPF_STMT(BPF_RET + BPF_K, 0x7fffffff),
 | 
			
		||||
    };
 | 
			
		||||
    static const struct sock_fprog sfp_arp = {
 | 
			
		||||
        .len = sizeof sf_arp / sizeof sf_arp[0],
 | 
			
		||||
        .filter = sf_arp,
 | 
			
		||||
    };
 | 
			
		||||
    int ret = setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &sfp_arp,
 | 
			
		||||
                         sizeof sfp_arp) != -1;
 | 
			
		||||
    if (ret < 0)
 | 
			
		||||
        log_warning("%s: Failed to set BPF for basic ARP socket: %s",
 | 
			
		||||
                    client_config.interface, strerror(errno));
 | 
			
		||||
    return ret == 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool arp_set_bpf_defense(int fd, uint32_t client_addr,
 | 
			
		||||
                                uint8_t client_mac[6])
 | 
			
		||||
{
 | 
			
		||||
    uint32_t mac4b;
 | 
			
		||||
    uint16_t mac2b;
 | 
			
		||||
    memcpy(&mac4b, client_mac, 4);
 | 
			
		||||
    memcpy(&mac2b, client_mac + 4, 2);
 | 
			
		||||
 | 
			
		||||
    struct sock_filter sf_arp[] = {
 | 
			
		||||
        // Verify that the frame has ethernet protocol type of ARP
 | 
			
		||||
        // and that the ARP hardware type field indicates Ethernet.
 | 
			
		||||
        BPF_STMT(BPF_LD + BPF_W + BPF_ABS, 12),
 | 
			
		||||
        BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, (ETH_P_ARP << 16) | ARPHRD_ETHER,
 | 
			
		||||
                 1, 0),
 | 
			
		||||
        BPF_STMT(BPF_RET + BPF_K, 0),
 | 
			
		||||
        // Verify that the ARP protocol type field indicates IP, the ARP
 | 
			
		||||
        // hardware address length field is 6, and the ARP protocol address
 | 
			
		||||
        // length field is 4.
 | 
			
		||||
        BPF_STMT(BPF_LD + BPF_W + BPF_ABS, 16),
 | 
			
		||||
        BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, (ETH_P_IP << 16) | 0x0604, 1, 0),
 | 
			
		||||
        BPF_STMT(BPF_RET + BPF_K, 0),
 | 
			
		||||
 | 
			
		||||
        // If the ARP packet source IP does not match our IP address, then
 | 
			
		||||
        // it can be ignored.
 | 
			
		||||
        BPF_STMT(BPF_LD + BPF_W + BPF_ABS, 28),
 | 
			
		||||
        BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, client_addr, 1, 0),
 | 
			
		||||
        BPF_STMT(BPF_RET + BPF_K, 0),
 | 
			
		||||
        // If the first four bytes of the ARP packet source hardware address
 | 
			
		||||
        // does not equal our hardware address, then it's a conflict and should
 | 
			
		||||
        // be passed along.
 | 
			
		||||
        BPF_STMT(BPF_LD + BPF_W + BPF_ABS, 22),
 | 
			
		||||
        BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, mac4b, 1, 0),
 | 
			
		||||
        BPF_STMT(BPF_RET + BPF_K, 0x7fffffff),
 | 
			
		||||
        // If the last two bytes of the ARP packet source hardware address
 | 
			
		||||
        // do not equal our hardware address, then it's a conflict and should
 | 
			
		||||
        // be passed along.
 | 
			
		||||
        BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 26),
 | 
			
		||||
        BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, mac2b, 1, 0),
 | 
			
		||||
        BPF_STMT(BPF_RET + BPF_K, 0x7fffffff),
 | 
			
		||||
        // Packet announces our IP address and hardware address, so it requires
 | 
			
		||||
        // no action.
 | 
			
		||||
        BPF_STMT(BPF_RET + BPF_K, 0),
 | 
			
		||||
    };
 | 
			
		||||
    struct sock_fprog sfp_arp = {
 | 
			
		||||
        .len = sizeof sf_arp / sizeof sf_arp[0],
 | 
			
		||||
        .filter = (struct sock_filter *)sf_arp,
 | 
			
		||||
    };
 | 
			
		||||
    int ret = setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &sfp_arp,
 | 
			
		||||
                          sizeof sfp_arp) != -1;
 | 
			
		||||
    if (ret < 0)
 | 
			
		||||
        log_warning("%s: Failed to set BPF for defense ARP socket: %s",
 | 
			
		||||
                    client_config.interface, strerror(errno));
 | 
			
		||||
    return ret == 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int create_arp_defense_socket(uint32_t client_addr,
 | 
			
		||||
                                     uint8_t client_mac[6], bool *using_bpf)
 | 
			
		||||
{
 | 
			
		||||
    assert(using_bpf);
 | 
			
		||||
    int fd = create_arp_socket();
 | 
			
		||||
    *using_bpf = arp_set_bpf_defense(fd, client_addr, client_mac);
 | 
			
		||||
    return fd;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int create_arp_basic_socket(bool *using_bpf)
 | 
			
		||||
{
 | 
			
		||||
    assert(using_bpf);
 | 
			
		||||
    int fd = create_arp_socket();
 | 
			
		||||
    *using_bpf = arp_set_bpf_basic(fd);
 | 
			
		||||
    return fd;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// XXX: Can share with ifch
 | 
			
		||||
static void setup_signals_sockd(void)
 | 
			
		||||
{
 | 
			
		||||
@@ -427,7 +531,24 @@ static size_t execute_sockd(char *buf, size_t buflen)
 | 
			
		||||
        return 1;
 | 
			
		||||
    }
 | 
			
		||||
    case 'U': xfer_fd(create_udp_listen_socket(), 'U'); return 1;
 | 
			
		||||
    case 'a': xfer_fd(create_arp_socket(), 'a'); return 1;
 | 
			
		||||
    case 'a': {
 | 
			
		||||
        bool using_bpf;
 | 
			
		||||
        int fd = create_arp_basic_socket(&using_bpf);
 | 
			
		||||
        xfer_fd(fd, using_bpf ? 'A' : 'a'); return 1;
 | 
			
		||||
    }
 | 
			
		||||
    case 'd': {
 | 
			
		||||
        uint32_t client_addr;
 | 
			
		||||
        uint8_t client_mac[6];
 | 
			
		||||
        bool using_bpf;
 | 
			
		||||
        if (buflen < 1 + sizeof client_addr + 6)
 | 
			
		||||
            return 0;
 | 
			
		||||
        memcpy(&client_addr, buf + 1, sizeof client_addr);
 | 
			
		||||
        memcpy(client_mac, buf + 1 + sizeof client_addr, 6);
 | 
			
		||||
        int fd = create_arp_defense_socket(client_addr, client_mac,
 | 
			
		||||
                                           &using_bpf);
 | 
			
		||||
        xfer_fd(fd, using_bpf ? 'D' : 'd');
 | 
			
		||||
        return 11;
 | 
			
		||||
    }
 | 
			
		||||
    case 's': xfer_fd(create_raw_broadcast_socket(), 's'); return 1;
 | 
			
		||||
    case 'u': {
 | 
			
		||||
        uint32_t client_addr;
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user